C言語におけるポインタの基礎

メモリアドレスとは

C言語で重要な概念の一つに「アドレス」があります。メモリ(ここでは物理メモリではなく、仮想的な論理メモリ空間を指します)は、1バイトごとに一意な番号が割り当てられています。たとえば、定規の目盛りをイメージしてください。0から999までの目盛りが刻まれた定規で、35mmや256mmの位置を正確に指し示せます。メモリも同様に線形に配置されており、32ビットOSでは0から4,294,967,295までの範囲が存在します。この番号をアドレスと呼び、通常は16進数(例: 0x12ff7c)で表記します。

C言語では、定義された変数ごとにメモリ上の領域(int型は4バイト、char型は1バイトなど)が確保され、それぞれのバイトにアドレスが付与されます。アドレス演算子 & を用いることで、変数のアドレスを取得できます。以下のコード例をご覧ください。

#include <stdio.h>

int main()
{
    int idx;
    int vals[10] = {1,2,3,4,5,6,7,8,9,0};
    char chars[10] = {'c','l','a','n','g','u','a','g','e','\0'};

    for (idx = 0; idx < 10; idx++)
    {
        printf("int Addr: 0x%x, Value: %d\n", &vals[idx], vals[idx]);
    }

    printf("\n");

    for (idx = 0; idx < 10; idx++)
    {
        printf("char Addr: 0x%x, Value: %c\n", &chars[idx], chars[idx]);
    }

    return 0;
}

ポインタの基本

アドレスが「定数」であるのに対し、ポインタは「変数」としてアドレスを格納します。ポインタ変数は、メモリ上の「遊標」のように、指し示す場所を変更できます。ポインタ変数の宣言は以下の形式で行います。

型指定子 *変数名;

ここで * はポインタであることを示し、型指定子 はポインタが指す先のデータ型を表します。また、間接参照演算子 * を用いて、ポインタが指すアドレスに格納された値を取得できます。

#include <stdio.h>

int main()
{
    int value = 2014;
    int *ptr = &value;

    printf("value Addr = 0x%x, value = %d\n", &value, value);
    printf("ptr = 0x%x, *ptr = %d\n", ptr, *ptr);
    printf("間接参照: %d\n", *&value);

    return 0;
}

配列とポインタ

配列の要素には添字でアクセスできるほか、ポインタを使ってアクセスすることも可能です。配列名は配列の先頭アドレス(0番要素のアドレス)を表す定数です。以下はその例です。

int data[10];          /* 10個の整数配列 */
int *ptr;              /* 整数型ポインタ */
ptr = &data[0];        /* 先頭要素のアドレスを代入 */

C言語では、data&data[0] と等価です。よって、次のように書けます。

ptr = data;            /* ptr = &data[0] と同じ */
int *ptr2 = data;      /* 宣言と同時に初期化 */

ポインタ ptr が先頭要素を指しているとき、ptr + idata[i] のアドレス、*(ptr + i) はその値を表します。ポインタ演算の結果、ptr + 1 は次の要素(data[1])を指します。

配列要素へのアクセス方法は2通りあります。

  • 添字法: data[i] の形式
  • ポインタ法: *(data + i) または *(ptr + i) の形式
#include <stdio.h>

int main()
{
    int i;
    int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    int *p = arr;

    for (i = 0; i < 10; i++)
    {
        printf("p: %d , arr: %d\n", *(p++), *(arr + i));
    }

    printf("\n");
    return 0;
}

注意:ポインタ変数は ++-- で自身の値を変更できますが、配列名(arr)は定数なので値を変更できません。

文字列とポインタ

文字列は文字配列または文字ポインタで扱えます。文字ポインタを使って文字列定数を指すことができます。

char *str = "example.com/blog";

これはポインタ str に文字列定数の先頭アドレスを格納します。一方、文字配列を使う方法もあります。

char buffer[] = "example.com/blog";

ここで buffer は配列名で、文字列の先頭アドレスを表します。

両者には重要な違いがあります。文字ポインタ str は変数なので別の文字列を指すように変更できますが、指し先の文字列定数の内容を変更することはできません。一方、文字配列 buffer は要素の内容を変更できます(例: buffer[0] = 'E';)。この点を理解しておくことが重要です。

タグ: C言語 ポインタ メモリアドレス 配列 文字列

8月1日 16:35 投稿