6

配列を関数に渡す必要がある場合、関数の次のすべての宣言が機能するようです

void f(int arr[])  
void f(int arr[4]) // is this one correct?

このため:

int a[]={1,2,3,4};
f(a);

しかし、配列を別の配列に割り当てると、失敗します

int a[]={1,2,3,4};
int b[4] = a; // error: array must be initialized with a brace-enclosed initializer

では、なぜ関数の引数として渡された配列は問題ないのに、単純な割り当てのrhsで使用されるのは間違っているのでしょうか。

4

7 に答える 7

12

違いを理解するには、2つの異なるコンテキストを理解する必要があります。

  • のコンテキストでは、型の配列の名前は型へTのポインターとT同等であり、配列の最初の要素へのポインターと同じです。
  • オブジェクトコンテキストでは、型の配列の名前はポインタTになりません。

オブジェクトコンテキストとは何ですか?

a = b;aはオブジェクトコンテキストにあります。変数のアドレスを取得すると、オブジェクトコンテキストで使用されます。最後にsizeof、変数で演算子を使用すると、オブジェクトコンテキストで使用されます。他のすべての場合、変数は値のコンテキストで使用されます。

これで、この知識が得られました。

void f(int arr[4]);

まったく同じです

void f(int *arr);

ご存知のように、関数宣言からサイズ(上記の4)を省略できます。これは、に渡される「配列」のサイズがわからないことを意味しますf()。後で、あなたがするとき:

int a[]={1,2,3,4};
f(a);

関数呼び出しでは、名前aは値コンテキストにあるため、へのポインターになりintます。fへのポインタを期待しているので、これは良いことですint。したがって、関数の定義と使用は一致します。渡されるのは、 ( )f()の最初の要素へのポインタです。a&a[0]

の場合

int a[]={1,2,3,4};
int b[4] = a;

名前bはオブジェクトコンテキストで使用され、ポインタに還元されません。(ちなみに、aここ値のコンテキストにあり、ポインターになります。)

ここで、int b[4];4秒のストレージに相当するストレージを割り当てint、それに名前bを付けます。 a同様のストレージも割り当てられました。したがって、実際には、上記の割り当ては「保管場所を以前の場所と同じにしたい」という意味です。これは意味がありません。

の内容をにコピーする場合は、次のようにすることができます。ab

#include <string.h>
int b[4];
memcpy(b, a, sizeof b);

または、以下を指すポインタbが必要な場合a

int *b = a;

ここで、aは値のコンテキストにあり、へのポインタに還元されるためint、に割り当てることができaますint *

最後に、配列を初期化するときに、明示的な値を割り当てることができます。

int a[] = {1, 2, 3, 4};

ここで、aには1、2、3、および4に初期化された4つの要素があります。次のこともできます。

int a[4] = {1, 2, 3, 4};

リスト内の要素が配列内の要素数よりも少ない場合、残りの値は0と見なされます。

int a[4] = {1, 2};

a[2]a[3]を0に設定します。

于 2010-01-09T22:42:42.213 に答える
7
void f(int arr[]);
void f(int arr[4]);

構文は誤解を招きます。それらは両方ともこれと同じです:

void f(int *arr);

つまり、配列の先頭へのポインタを渡しています。アレイをコピーしていません。

于 2010-01-09T21:45:09.023 に答える
6

Cは配列の割り当てをサポートしていません。関数呼び出しの場合、配列はポインターに減衰します。Cはポインタの割り当てをサポートします。これはほぼ毎日ここで尋ねられます-これを説明していないあなたたちが読んでいるCの教科書は何ですか?

于 2010-01-09T21:45:27.337 に答える
3

memcpyを試してください。

int a[]={1,2,3,4};
int b[4];
memcpy(b, a, sizeof(b));

それを指摘してくれてありがとう、スティーブ、私がCを使ってからしばらく経ちました。

于 2010-01-09T21:48:07.650 に答える
1

それについて直感的に理解するには、マシンレベルで何が起こっているのかを理解する必要があります。

初期化セマンティクス(= {1,2,3,4})は、「この方法でバイナリイメージに正確に配置する」ことを意味するため、これをコンパイルできます。

配列の割り当ては異なります。コンパイラはそれをループに変換する必要があり、実際には要素を反復処理します。Cコンパイラ(またはC ++)はそのようなことを決してしません。それは当然あなたが自分でそれをすることを期待しています。なんで?できるから。したがって、C(memcpy)で記述されたサブルーチンである必要があります。これはすべて、CとC++である武器へのシンプルさと近さに関するものです。

于 2010-01-09T23:30:29.753 に答える
0

ainのタイプint a[4]は。であることに注意してくださいint [4]

ただし、TypeOf&a)== int (*)[4]!= int [4]

また、の値のタイプaint *、上記のすべてとは異なることに注意してください。

試すことができるサンプルプログラムは次のとおりです。

int main() {
  // All of these are different, incompatible types!      
  printf("%d\n", sizeof (int[4]));  // 16
  // These two may be the same size, but are *not* interchangeable!
  printf("%d\n", sizeof (int (*)[4]));  // 4
  printf("%d\n", sizeof (int *));  // 4
}
于 2010-04-05T00:49:15.063 に答える
0

明確にしたい。回答には誤解を招くヒントがいくつかあります...次のすべての関数は整数配列を取ることができます。

void f(int arr[])
void f(int arr[4])
void f(int *arr) 

しかし、正式な議論は同じではありません。したがって、コンパイラーはそれらを異なる方法で処理できます。内部メモリ管理の意味では、すべての引数がポインタにつながります。

void f(int arr[])

... f()は任意のサイズの配列を取ります。

void f(int arr[4])

...仮引数は、配列サイズを示します。

void f(int *arr)

...整数ポインタを渡すこともできます。f()はサイズについて何も知りません。

于 2016-10-14T15:19:35.170 に答える