web-dev-qa-db-ja.com

Cの関数に配列と配列ポインターを渡すことの違い

Cの2つの関数の違いは何ですか?

void f1(double a[]) {
   //...
}

void f2(double *a) {
   //...
}

かなり長い配列で関数を呼び出すと、これらの2つの関数の動作は異なりますか、スタック上のスペースが増えますか?

101
Kaushik Shankar

まず、いくつかの 標準

6.7.5.3関数宣言子(プロトタイプを含む)
...
7「 'の配列」としてのパラメーターの宣言 タイプ」を「修飾された タイプ型修飾子(存在する場合)は、配列型の派生の[および]内で指定されたものです。キーワードstaticが、配列型の派生の[および]内にも現れる場合、関数の呼び出しごとに、対応する実際の引数の値は、サイズ式で指定された数以上の要素を持つ配列の最初の要素。

つまり、要するに、T a[]またはT a[N]として宣言された関数パラメーターはすべて、あたかもT *aと宣言されたものとして扱われます。

それでは、配列パラメータがポインタとして宣言されているように扱われるのはなぜですか?その理由は次のとおりです。

6.3.2.1左辺値、配列、関数指定子
...
3 sizeof演算子または単項&演算子のオペランドである場合、または配列を初期化するために使用される文字列リテラルである場合を除き、 '' array型の式の タイプ」は、「へのポインター」タイプの式に変換されます タイプ』は、配列オブジェクトの初期要素を指し、左辺値ではありません。配列オブジェクトにレジスタストレージクラスがある場合、動作は未定義です。

次のコードを考えます:

int main(void)
{
  int arr[10];
  foo(arr);
  ...
}

fooの呼び出しでは、配列式arrsizeofまたは&のオペランドではないため、その型は暗黙的に "10- 6.2.3.1/3に従ってint "の要素配列から" intへのポインタ "に。したがって、fooは配列値ではなくポインター値を受け取ります。

6.7.5.3/7のため、次のようにfooと書くことができます。

void foo(int a[]) // or int a[10]
{
  ...
}

しかし、それは次のように解釈されます

void foo(int *a)
{
  ...
}

したがって、2つの形式は同じです。

6.7.5.3/7の最後の文はC99で導入され、基本的に次のようなパラメータ宣言がある場合

void foo(int a[static 10])
{
  ...
}

aに対応する実際のパラメーターは、少なくとも 10要素の配列でなければなりません。

107
John Bode

違いは純粋に構文的なものです。 Cでは、配列表記が関数パラメーターに使用されると、自動的にポインター宣言に変換されます。

28
Thomas Pornin

いいえ、それらの間に違いはありません。テストするために、このCコードをDev C++(mingw)コンパイラで作成しました。

#include <stdio.h>

void function(int* array) {
     int a =5;
}

void main() {  
     int array[]={2,4};
     function(array);
     getch();
}

IDAのバイナリファイルの両方の呼び出しバージョンの.exeでmain functionを逆アセンブルすると、次のようなまったく同じアセンブリコードが取得されます。

Push    ebp
mov     ebp, esp
sub     esp, 18h
and     esp, 0FFFFFFF0h
mov     eax, 0
add     eax, 0Fh
add     eax, 0Fh
shr     eax, 4
shl     eax, 4
mov     [ebp+var_C], eax
mov     eax, [ebp+var_C]
call    sub_401730
call    sub_4013D0
mov     [ebp+var_8], 2
mov     [ebp+var_4], 4
lea     eax, [ebp+var_8]
mov     [esp+18h+var_18], eax
call    sub_401290
call    _getch
leave
retn

したがって、この呼び出しの2つのバージョンに違いはありません。少なくともコンパイラーはそれらを等しく脅します。

0
mcaaltuntas