banner
yono

yono

哈喽~欢迎光临
follow
github

关于 C语言 指针的推进量

关于#

为了避免误会,我很少使用类似这样的语句

uint32_t *p;
p++;
p+=4;

而是仅使用这样的形式

uint32_t *p;
uint32_t d;
d = p[4];
uint32_t *pt;
pt = &p[4];

但是在遍历推演时,后一个形式一定需要一个新变量进行遍历。
而前一个形式则可以在函数传参指针的基础上进行,O0 下一定会比后一个形式节省堆栈。

最近为了提升我的库性能,决定牺牲一些可读性,改为不直观的写法,所以有后续的测试进行。

结论放在前面,当指针 + 这个动作发生时,被 + 处理的指针类型决定了实际推进了多少 RAM 地址,直观见下表

指针类型RAM 地址推进量
void* + 11
uint8_t* + 11
uint16_t* + 12
uint32_t* + 14
其他及结构体指针 + 1依据类型大小

址成数值#

使用如下代码进行简单测试

#include <stdio.h>
#include <stdint.h>
#include <string.h>

uint8_t  u8t[]  = {0, 1, 2, 3, 4, 5, 6, 7, 8};
uint16_t u16t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
uint32_t u32t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};

/* 入口 */
int main(int argc, char *argv[])
{
    uint8_t  p8get;
    uint16_t p16get;
    uint32_t p32get;

    p8get  = u8t[1];                // = 1
    p8get  = *(uint8_t *)(u8t + 1); // = 1
    p8get  = u8t[2];                // = 2
    p8get  = *(uint8_t *)(u8t + 2); // = 2
    p8get  = u8t[3];                // = 3
    p8get  = *(uint8_t *)(u8t + 3); // = 3

    p16get = u16t[1];                 // = 1
    p16get = *(uint16_t *)(u16t + 1); // = 1
    p16get = u16t[2];                 // = 2
    p16get = *(uint16_t *)(u16t + 2); // = 2
    p16get = u16t[3];                 // = 3
    p16get = *(uint16_t *)(u16t + 3); // = 3

    p32get = u32t[1];                 // = 1
    p32get = *(uint32_t *)(u32t + 1); // = 1
    p32get = u32t[2];                 // = 2
    p32get = *(uint32_t *)(u32t + 2); // = 2
    p32get = u32t[3];                 // = 3
    p32get = *(uint32_t *)(u32t + 3); // = 3

    return 0;
}

当然,非常符合预期。例如指针 u32t,+3 就是访问第三个 buffer 值,非常合理.......吗?

依据众所周知对 RAM 的理解,一个地址存一个字节,那么u8t+1是推进了 1 个字节 u32t+1则会推进 4 个字节,+1这个操作产生了不同的结果。

址的数值#

验证以上,使用了如下代码进行简单测试

#include <stdio.h>
#include <stdint.h>
#include <string.h>

uint8_t  u8t[]  = {0, 1, 2, 3, 4, 5, 6, 7, 8};
uint16_t u16t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
uint32_t u32t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};

/* 入口 */
int main(int argc, char *argv[])
{
    uint8_t  *p8get;
    uint16_t *p16get;
    uint32_t *p32get;

    p8get  = u8t;  // = 0x7FF6757b4000 <u8t>
    p16get = u16t; // = 0x7FF6757b4010 <u16t>
    p32get = u32t; // = 0x7FF6757b4040 <u32t>

    p8get  = u8t + 1;  // = 0x7FF6757b4001 <u8t+1>
    p16get = u16t + 1; // = 0x7FF6757b4012 <u16t+2>
    p32get = u32t + 1; // = 0x7FF6757b4044 <u32t+4>

    p8get  = u8t + 2;  // = 0x7FF6757b4002 <u8t+2>
    p16get = u16t + 2; // = 0x7FF6757b4014 <u16t+4>
    p32get = u32t + 2; // = 0x7FF6757b4048 <u32t+8>

    return 0;
}

可以看出,在指针代表的实际 RAM 地址上,其数值的推进确实是不一样的,哪怕使用的是相同的 +1

是 void*#

我们很多的函数,作为通用工具,是传入 void* 的,根据系统状态、标志条件的不同,将其作为不同的指针进行判定,那么有如下测试。

#include <stdio.h>
#include <stdint.h>
#include <string.h>

uint8_t  u8t[]  = {0, 1, 2, 3, 4, 5, 6, 7, 8};
uint16_t u16t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
uint32_t u32t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};

/* 入口 */
int main(int argc, char *argv[])
{
    void    *pVget;

    uint64_t pVV;

    pVget = (void *)u8t; // = 0x7FF61F184000 <u8t>
    pVget++;
    pVV   = (uint64_t)pVget; // = 0x7FF61F184001 <u8t+1>

    pVget = (void *)u16t; // = 0x7FF61F184010 <u16t>
    pVget++;
    pVV   = (uint64_t)pVget; // = 0x7FF61F184011 <u16t+1>

    pVget = (void *)u32t; // = 0x7FF61F184040 <u32t>
    pVget++;
    pVV = (uint64_t)pVget; // 0x7FF61F184041 <u32t+1>

    return 0;
}

可以看到 void* 与实际 RAM 的行为模式完全一致,+n 就是 + n。

假装不是 void*#

如果将 void* 作为不同的类型指针进行解析,有如下测试

#include <stdio.h>
#include <stdint.h>
#include <string.h>

uint8_t  u8t[]  = {0, 1, 2, 3, 4, 5, 6, 7, 8};
uint16_t u16t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
uint32_t u32t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};

/* 入口 */
int main(int argc, char *argv[])
{
    void *pVget;

    pVget = u8t;                    // = 0x7FF69FF64000 <u8t>
    pVget = (uint8_t *)pVget + 1;   // = 0x7FF69FF64001 <u8t+1>
    pVget = u8t;                    // = 0x7FF69FF64000 <u8t>
    pVget = (uint8_t *)(pVget + 1); // = 0x7FF69FF64001 <u8t+1>

    pVget = u16t;                    // = 0x7FF69FF64010 <u16t>
    pVget = (uint16_t *)pVget + 1;   // = 0x7FF69FF64012 <u16t+2>
    pVget = u16t;                    // = 0x7FF69FF64010 <u16t>
    pVget = (uint16_t *)(pVget + 1); // = 0x7FF69FF64011 <u16t+1>

    pVget = u32t;                    // 0x7FF69FF64040 <u32t>
    pVget = (uint32_t *)pVget + 1;   // 0x7FF69FF64044 <u32t+4>
    pVget = u32t;                    // 0x7FF69FF64040 <u32t>
    pVget = (uint32_t *)(pVget + 1); // 0x7FF69FF64041 <u32t+1>

    return 0;
}

可以看到,当 + 这个动作发生时,被 + 处理的类型决定了实际推进了多少 RAM 地址

此文由 Mix Space 同步更新至 xLog
原始链接为 https://www.yono233.cn/posts/shoot/24_7_27_C


加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。