Cの2つの関数の違いは何ですか?
void f1(double a[]) {
//...
}
void f2(double *a) {
//...
}
かなり長い配列で関数を呼び出すと、これらの2つの関数の動作は異なりますか、スタック上のスペースが増えますか?
まず、いくつかの 標準 :
6.7.5.3関数宣言子(プロトタイプを含む)
...
7「 'の配列」としてのパラメーターの宣言 タイプ」を「修飾された タイプ型修飾子(存在する場合)は、配列型の派生の[
および]
内で指定されたものです。キーワードstatic
が、配列型の派生の[
および]
内にも現れる場合、関数の呼び出しごとに、対応する実際の引数の値は、サイズ式で指定された数以上の要素を持つ配列の最初の要素。
つまり、要するに、T a[]
またはT a[N]
として宣言された関数パラメーターはすべて、あたかもT *a
と宣言されたものとして扱われます。
それでは、配列パラメータがポインタとして宣言されているように扱われるのはなぜですか?その理由は次のとおりです。
6.3.2.1左辺値、配列、関数指定子
...
3sizeof
演算子または単項&
演算子のオペランドである場合、または配列を初期化するために使用される文字列リテラルである場合を除き、 '' array型の式の タイプ」は、「へのポインター」タイプの式に変換されます タイプ』は、配列オブジェクトの初期要素を指し、左辺値ではありません。配列オブジェクトにレジスタストレージクラスがある場合、動作は未定義です。
次のコードを考えます:
int main(void)
{
int arr[10];
foo(arr);
...
}
foo
の呼び出しでは、配列式arr
はsizeof
または&
のオペランドではないため、その型は暗黙的に "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要素の配列でなければなりません。
違いは純粋に構文的なものです。 Cでは、配列表記が関数パラメーターに使用されると、自動的にポインター宣言に変換されます。
いいえ、それらの間に違いはありません。テストするために、この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つのバージョンに違いはありません。少なくともコンパイラーはそれらを等しく脅します。