17.2 配列とポインタ

今まで配列名そのものは何の意味も持ってこなかった。例えば、以下のように 宣言された配列では、

    int array[20];

配列名 array はプログラム中では何の積極的な意味を持たなかったし、 配列名を利用する方法もなかった。実はタネを明かせば、この配列名はポインタの ようなものに なっているのである。つまり、array は、&array[0] と同じになって いるのである。

\epsfile{file=17fig2}

例 2
    int array[20];
    printf("Example. address of array\n");
    printf("array %u:  =  &array[0]: %u\n", array, &array[0]);

この例を実行すれば、まったく同じアドレスを指している事が分かる。 しかし、通常のポインタ変数と違い、配列名へのアドレスの代入は許されていない。 そうした意味で、配列名はポインタと少し違っていて、 あるアドレスを固定的に指す定数であると思えば良い。

間違った例
    int array[20];
    int a;
    array = &a;

参考  この例が間違っている理由は、先に述べたように配列名には代入が許されないからで ある。C 言語では、こうした代入が許されないが、参照は許される(式の右辺には使う 事が出来る) 値を右辺値と呼んでいる。一方、代入が許される(つまり、式の左辺に 使う事が出来る) 値を左辺値と呼ぶ。勿論、左辺値は式の右辺にも使うことが出来るので 左辺値は右辺値を含んでいると言える。

さて、それでは、配列の要素へのアクセスはどのようにしてなされるのだろうか。 前の章で学んだようにポインタへの整数演算は、型に応じてされるので、

となっているのである。従って、一般に配列要素とアドレスとの関係は次のように なっている。

        array[n]  <=>   *(array + n)

そして、C 言語では上の関係は左から右だけではなく、右から左にも有効なのである。 つまり、

例 3
    char *s = "array";
    int n=0;
    while(s[n]!='\0'){
        putchar(s[n]);
        n++;
    }

というプログラムは、次のプログラムとほぼ同じである事を意味している。

例 4
char s[] = {'a', 'r', 'r', 'a', 'y','\0'};
main(){ 
    char *p=s;
    while(*p!='\0'){
        putchar(*p);
        p++;
    }
}

しかし、以前に学んだように配列の初期化は一般には許されず、静的または外的 な場合にのみ許されていることに注意しよう(静的、外的な場合以外でも初期化を 許す処理系もある)。

つまり、C 言語では配列はポインタを用いて実現されているのである。

参考  もっと積極的に言えば、C 言語では、配列要素へのアクセスは常に、 array[n] => *(array+n) に展開される。このことは次のような奇妙な例を 実行することで明らかとなる。加算は常に交換可能である、即ち $a+b=b+a$ である。従って、 array[n] => *(array+n) => *(n+array) => n[array] となるので、

参考例
char s[] = {'a', 'r', 'r', 'a', 'y','\0'};
main(){ 
    int n=0;
    while(n[s]!='\0'){
        putchar(n[s]);
        n++;
    }
}

は、先の例とまったく同じに正常に動作する!



最初のページ 戻る 次へ 最後のページ 目次
Hiroyasu Asami