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 は第三のバッファ値にアクセスすることを意味し、非常に合理的です.......ですか?

RAM に関する一般的な理解に基づくと、1 つのアドレスは 1 バイトを格納しますので、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


読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。