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


載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。