风过空庭,字句正徐来。
关于关于本站关于我给我点钱
更多时间线友链文件服务wiki
联系写留言发邮件GitHub
© 2024-2026 yono. | RSS 订阅 | 站点地图 | | Stay hungry. Stay foolish.
Powered by Mix Space&
白い
.
| 粤 ICP 备2024284785号-1 |
正在被0人看爆
纸白微明,未成篇章。

关于 C语言 指针的推进量

(已编辑)
/
84
1
AI·GEN

关键洞察

这篇文章上次修改于,可能部分内容已经不适用,如有疑问可询问作者。

关于 C语言 指针的推进量

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • 关于

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

    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* + 1 1
    uint8_t* + 1 1
    uint16_t* + 1 2
    uint32_t* + 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[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* 的,根据系统状态、标志条件的不同,将其作为不同的指针进行判定,那么有如下测试。

    CodeBlock Loading...

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

    假装不是 void*

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

    CodeBlock Loading...

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

    特别的

    对于 DSP 芯片,一个字节是 16 位,所以 (uint32_t *) 类型 +1 动作,推进是 2 个地址。

    #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;
    }
    
    #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;
    }