🌊

C言語のポインタについて

2024/06/05に公開

C言語のポインタにはメモリのアドレスが入ってます。それだけ知っていれば十分です。
32bitのCPUなら32bitの数値型、64bitのCPUなら64bitの数値型です。
型情報に限らず全てメモリのアドレスです。それは単なる数値でもあります。

int main(int argc, char** argv)
{
    char c = 'c';
    char* p = &c;

    long n = (long)p;

    char* p2 = (char*)n;

    putc(*p2);

    return 0;
}

これは動きます。longが64bitの処理系でCPUが64bitであれば。
pは単なる64bitの数値です。

型情報はdereffrenceするときに使われます。
このときchar*ならchar型にdereffrenceされchar**ならchar*にdereffrenceされます。
このときメモリにどんな型のメモリが入っているかはユーザーが把握していないといけません。
逆に言えば、メモリの情報さえ正しければ、構造体にもキャストできます。

struct sData
{
   int a;
   int b;
};

int main(int argc, char** p)
{
    int data[2] = { 111, 222 };
    char* p = (char*)data;

    struct sData* pdata = (struct sData*)p;

    printf("%d %d¥n", pdata->a, pdata->b);

    struct sData data2 = *pdata;

    printf("%d %d¥n", data2.a, data2.b);

    return 0;
}

多分これは111と222を出力するでしょう。intの配列は32bitの111と222が並んだメモリの塊です。
struct sDataもintが32bitが2つ入ったメモリの塊です。
ので、互換性があるので、このコードは動きます。

あともう一個型情報が必要になるのは加算と減算です。このときアドレスという数値に加算されるのは型情報をもとに加算されます。

int main(int argc, char** argv)
{
    int data[2] = { 111, 222 };

    int* p = data;

    p++;    // pはdataを指していたがint型のメモリの大きさだけ加算される+4

    char* p2 = (char*)data;

    p2++;   // pはdataを指していたがchar型のメモリの大きさだけ加算される。+1
     
    return 0;
}

pは*pすると222になりますが、p2は*p2すると111(32bit)222(32bit)というメモリの1バイト進めたとこでの1バイトの数値を返します。(charの大きさは1バイト)

このようにポインタというものはCPUとメモリと密接した処理を行うためにあります。
例えばバイナリファイルを読み込むためにはElfのバイト情報を定義した構造体がelf.hというヘッダーに書かれていますが、バイナリファイルをopenして読み込みchar型の配列に代入して、そのchar型の配列のアドレスの一番上をstruct Elf(名前は適当)という構造体のポインタに代入すればバイナリファイルの情報を読み込めたりします。普通の言語みたいに構造体のフィールドに全て値を入れたりする必要がないです。ポインタだと読み込みが一気にできます。
こういうことは普通の言語では難しいため、C言語がいまだに使われている理由となっています。

参考になれば幸いです。

Discussion