Cの2つの関数の違いは何ですか?
void f1(double a[]) {
//...
}
void f2(double *a) {
//...
}
かなり長い配列で関数を呼び出す場合、これら2つの関数の動作は異なりますか?スタック上でより多くのスペースを使用しますか?
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左辺値、配列、および関数指定子
... 3演算子または単項演算子
のオペランドである場合、または配列を初期化するために使用される文字列リテラルである場合を除き、「型の配列」型の式'は、配列オブジェクトの初期要素を指し、左辺値ではない' '型''ポインタから型''の式に変換されます。配列オブジェクトにレジスタストレージクラスがある場合、動作は未定義です。sizeof
&
次のコードが与えられます:
int main(void)
{
int arr[10];
foo(arr);
...
}
の呼び出しでfoo
は、配列式arr
はまたはのいずれのオペランドでもないsizeof
ため、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
を持つ配列である必要があります。
違いは純粋に構文的なものです。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でメイン関数を逆アセンブルすると、次のようなまったく同じアセンブリコードが得られます。
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つのバージョンに違いはありません。少なくとも、コンパイラーはそれらを等しく脅かします。