配列と関数を扱う際にはパターンがあります。最初は少し見づらいです。
配列を処理するときは、次のことを覚えておくと便利です。ほとんどのコンテキストで配列式が現れると、式の型は「TのN要素配列」から「Tへのポインター」に暗黙的に変換され、その値が設定されます。配列の最初の要素を指すようにします。この規則の例外は、配列式が&
or sizeof
演算子のオペランドとして表示される場合、またはそれが宣言で初期化子として使用されている文字列リテラルである場合です。
したがって、配列式を引数として関数を呼び出すと、関数は配列ではなくポインタを受け取ります。
int arr[10];
...
foo(arr);
...
void foo(int *arr) { ... }
これが、「%s」に対応する引数に演算子を使用しない理由です:&
scanf()
char str[STRING_LENGTH];
...
scanf("%s", str);
暗黙的な変換のため、配列の先頭を指す値をscanf()
受け取りchar *
ますstr
。これは、配列式を引数として呼び出されるすべての関数に当てはまります(str*
関数*scanf
や*printf
関数などについてのみ)。
実際には、次のように、&
演算子を使用して配列式を持つ関数を呼び出すことはおそらくないでしょう。
int arr[N];
...
foo(&arr);
void foo(int (*p)[N]) {...}
このようなコードはあまり一般的ではありません。関数宣言で配列のサイズを知っている必要があり、関数は特定のサイズの配列へのポインターでのみ機能します(Tの10要素配列へのポインターは、11要素配列へのポインターとは異なるタイプです) Tの)。
配列式が&
演算子のオペランドとして表示される場合、結果の式の型は「TのN要素配列へのポインター」またはT (*)[N]
であり、これはポインターの配列(T *[N]
)および基本型へのポインター(T *
)。
関数とポインタを処理する場合、覚えておくべきルールは次のとおりです。引数の値を変更して、それを呼び出しコードに反映させる場合は、変更したいものへのポインタを渡す必要があります。繰り返しになりますが、配列は少しモンキーレンチを動作させますが、最初に通常のケースを扱います。
Cはすべての関数引数を値で渡すことに注意してください。仮パラメーターは、実パラメーターの値のコピーを受け取ります。仮パラメーターに対する変更は、実パラメーターには反映されません。一般的な例はスワップ関数です:
void swap(int x, int y) { int tmp = x; x = y; y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(a, b);
printf("after swap: a = %d, b = %d\n", a, b);
次の出力が得られます。
スワップ前:a = 1、b = 2
スワップ後:a = 1、b = 2
仮パラメータx
とy
から別個のオブジェクトであるa
とb
、そうに変化x
し、y
に反映されていないa
とb
。a
and の値を変更したいので、それらへのポインターをswap関数にb
渡す必要があります。
void swap(int *x, int *y) {int tmp = *x; *x = *y; *y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(&a, &b);
printf("after swap: a = %d, b = %d\n", a, b);
今あなたの出力は
スワップ前:a = 1、b = 2
スワップ後:a = 2、b = 1
swap関数では、x
およびの値を変更せずy
、what x
とy
pointの値を変更することに注意してください。への書き込みは、への書き込みと*x
は異なりx
ます。値x
自体は更新しません。場所を取得し、x
その場所の値を更新します。
ポインター値を変更する場合も同様です。私たちが書いたら
int myFopen(FILE *stream) {stream = fopen("myfile.dat", "r"); }
...
FILE *in;
myFopen(in);
次にstream
、stream
指すパラメータではなく、入力パラメータの値を変更するので、変更しstream
てもの値には影響しませんin
。これが機能するためには、ポインターをポインターに渡す必要があります。
int myFopen(FILE **stream) {*stream = fopen("myFile.dat", "r"); }
...
FILE *in;
myFopen(&in);
繰り返しになりますが、配列は少しモンキーレンチを作品に投げ込みます。配列式を関数に渡す場合、関数が受け取るのはポインターです。配列の添え字がどのように定義されているかにより、配列で使用できるのと同じように、ポインターで添字演算子を使用できます。
int arr[N];
init(arr, N);
...
void init(int *arr, int N) {size_t i; for (i = 0; i < N; i++) arr[i] = i*i;}
配列オブジェクトは割り当てられない場合があることに注意してください。つまり、次のようなことはできません
int a[10], b[10];
...
a = b;
そのため、配列へのポインタを扱う場合は注意が必要です。何かのようなもの
void (int (*foo)[N])
{
...
*foo = ...;
}
動作しません。