ページ 11

配列とアドレス演算子に関して

Posted: 2014年1月17日(金) 15:34
by array
配列名にアドレス演算子を作用させる操作は定義されているのでしょうか?

たとえば

コード:

  int a[2];
  int *p;
  p = (int *) malloc(2*sizeof(int));
  printf("a  : %p\n",  a);
  printf("&a : %p\n", &a);
  printf("p  : %p\n",  p);
  printf("&p : %p\n", &p);
を実行すると a と &a は同じ値になります。( p と &p は当然一般に異なる値になります。)

このコードにおいて、配列名 a は a[0] へのポインタになると思います。
しかしメモリ上に実体を持つポインタ変数 p とは違うのでアドレス演算子 & を作用させるのは未定義なのかなと思っていました。
しかし複数の環境で上のコードをためしたところどれも a = &a となります。

これは偶然でしょうか?
それともC言語では配列名にアドレス演算子を作用させる操作がこのように「配列名にアドレス演算子を作用させると、配列名と同じポインタ型の数値を返す」と定義されているのでしょうか?

Re: 配列とアドレス演算子に関して

Posted: 2014年1月17日(金) 15:58
by h2so5
array さんが書きました:配列名にアドレス演算子を作用させる操作は定義されているのでしょうか?

たとえば

コード:

  int a[2];
  int *p;
  p = (int *) malloc(2*sizeof(int));
  printf("a  : %p\n",  a);
  printf("&a : %p\n", &a);
  printf("p  : %p\n",  p);
  printf("&p : %p\n", &p);
を実行すると a と &a は同じ値になります。( p と &p は当然一般に異なる値になります。)

このコードにおいて、配列名 a は a[0] へのポインタになると思います。
aはポインタではなくて配列です。その証拠に、sizeof(a)はsizeof(int*)と異なります。
ですから、&aはa[0]へのポインタへのポインタではなく、配列へのポインタ(= a[0]へのポインタ)です。
また、配列は先頭へのポインタにキャストすることができます。

つまり、

コード:

(void *)a == (void *)&a
となります。

Re: 配列とアドレス演算子に関して

Posted: 2014年1月17日(金) 18:00
by ISLe
式の中に配列名が現れるとき、sizeof演算子のオペランド・アドレス演算子のオペランド・文字列リテラルのいずれかである場合を除き、配列の先頭要素を指すポインタに変換されます。
sizeof演算子のオペランド・アドレス演算子のオペランド・文字列リテラルのいずれかの場合は、配列全体を指します。

値が同じでも型が異なります。
aはintへのポインタ、&aはint[2]へのポインタ、です。

コード:

  int a[2];
  int *p;
  p = a; // &a[0]と同じ扱い
  p = &a; // エラー(Cでは警告) int (*)[2]からint *へ互換性のない変換 
(追記)
文字列リテラルに関して間違えました。
左辺値にできるかどうかとごっちゃにしてしまいました。