C言語標準から:
6.3.2.1.3 sizeof 演算子のオペランドまたは
単項 & 演算子、または初期化に使用される文字列リテラル
配列、タイプ ''array of type'' を持つ式は
''型へのポインタ'' 型の式に変換され、
配列オブジェクトの最初の要素を指しており、そうではありません
左辺値。配列オブジェクトが register ストレージ クラスを持っている場合、
動作は未定義です。
次のコードを想定します。
#include <stdio.h>
#include <string.h>
int main(void)
{
char foo[10] = {0};
char *p = foo;
foo[0] = 'b';
*(foo + 1) = 'a';
strcat(foo, "t");
printf("foo = %s, &foo = %p, &p = %p, sizeof foo = %lu, sizeof p = %lu\n",
foo, &foo, &p, (unsigned long) sizeof foo, (unsigned long) sizeof p);
return 0;
}
foo は、すべての要素が 0 に初期化された char の 10 要素の配列として宣言されます。p は、char へのポインターとして宣言され、foo を指すように初期化されます。
ラインで
char *p = foo;
式 foo の型は「char の 10 要素配列」です。foo は sizeof または & のオペランドではなく、配列の初期化に使用される文字列リテラルでもないため、その型は暗黙的に「char へのポインター」に変換され、配列の最初の要素を指すように設定されます。このポインタ値は p にコピーされます。
ラインで
foo[0] = 'b';
*(foo + 1) = 'a';
式 foo の型は「char の 10 要素配列」です。foo は sizeof または & のオペランドではなく、配列の初期化に使用される文字列リテラルでもないため、その型は暗黙的に「char へのポインター」に変換され、配列の最初の要素を指すように設定されます。添字式は「`*(foo + 0)」と解釈されます。
ラインで
strcat(foo, "t");
foo の型は「char の 10 要素配列」であり、文字列リテラル「t」の型は「char の 2 要素配列」です。どちらも sizeof または & のオペランドではないため、"t" は文字列リテラルですが、配列の初期化には使用されず、両方とも "pointer to char" 型に暗黙的に変換され、ポインター値はに渡されますstrcat()。
ラインで
printf("foo = %s, &foo = %p, &p = %p, sizeof foo = %lu, sizeof p = %lu\n",
foo, &foo, &p, (unsigned long) sizeof foo, (unsigned long) sizeof p);
foo の最初のインスタンスは、上記のように char へのポインターに変換されます。foo の 2 番目のインスタンスは & 演算子のオペランドであるため、その型は「char へのポインター」に変換されず、式「&foo」の型は「char の 10 要素配列へのポインター」または「char」です。 ( *
)[10]". これを、「char へのポインターへのポインター」または「char **
」である式「&p」の型 type と比較してください。foo の 3 番目のインスタンスは sizeof 演算子のオペランドであるため、その型は変換されず、sizeof は配列に割り当てられたバイト数を返します。これを、ポインターに割り当てられたバイト数を返す sizeof p の結果と比較します。
誰かがあなたに「配列はただのポインタだ」と言うときはいつでも、彼らは上で引用した標準のセクションを混乱させています。配列はポインターではなく、ポインターは配列ではありません。ただし、多くの場合、配列をポインターのように扱い、ポインターを配列のように扱うことができます。6 行目、7 行目、8 行目の "foo" を "p" に置き換えることもできます。ただし、sizeof または & のオペランドとしては交換できません。
編集:ところで、関数パラメータとして、
void foo(int *a);
と
void foo(int a[]);
同等です。「a[]」は「*
a」と解釈されます。これは、関数パラメーターに のみ当てはまることに注意してください。