Cの関数への配列と配列ポインターの受け渡しの違い


回答:


114

まず、いくつかの標準

(試作品を含む)6.7.5.3機能宣言子
...
'のアレイのようなパラメータの7 A宣言「」「に修飾ポインタ」に調整しなければならない 入力タイプ修飾子(もしあれば)は、これら指定されています「」、内[及び]配列型導出。キーワードstatic[]配列型の派生にも含まれている場合、関数の呼び出しごとに、対応する実引数の値により、少なくともサイズで指定された数の要素を持つ配列の最初の要素へのアクセスが提供されます。式。

だから、要するに、任意の関数のパラメータは、として宣言T a[]またはT a[N]扱われているかのように、それが宣言されましたT *a

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

6.3.2.1左辺値、配列、および関数指定子
...
3 sizeof演算子または単項&演算子のオペランドである場合、または配列を初期化するために使用される文字列リテラル、型がの配列」の式を除く『へのポインタ」型の表現に変換され、』配列オブジェクトの最初の要素を指すとは左辺値ではないことを'。配列オブジェクトにレジスタストレージクラスがある場合、動作は未定義です。

次のコードがあるとします:

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

への呼び出しでfooは、配列式arrsizeoforのオペランドではない&ため、その型は6.2.3.1/3に従って「10要素の配列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])
{
  ...
}

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


1
(少なくとも一部の古い)MSVC C ++コンパイラーを使用すると、コンパイラーが2つのケースで関数名を誤って異なる方法で処理するため(違いは同じであることを認識しながら)、リンクの問題が発生するため、違いがあります。ここでは、バグレポートを「修正されません」を参照してください。connect.microsoft.com/VisualStudio/feedback/details/326874/...を
greggo

29

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


1
@Kaushik:このケースでは同じですが、一般的なケースでは
BlueRaja-Danny Pflughoeft

@BlueRaja:はい、Cの落とし穴の1つです。関数パラメーターの宣言はローカル変数の宣言と非常に似ていますが、いくつかの微妙な違い(この配列からポインターへの自動変換など)があります。不注意なプログラマーを噛む傾向があります。
Thomas Pornin

0

いいえ、違いはありません。テストするために、この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のメイン関数を逆アセンブルすると、次のようにまったく同じアセンブリコードが得られます。

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つのバージョンに違いはありません。少なくともコンパイラはそれらを等しく脅かします。


18
申し訳ありませんが、これはgccの一部のバージョンが両方のx86で同じアセンブリを生成することを証明するだけです。正解、誤解。
lambdapower
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.