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 A宣言型「」「に修飾ポインタ」に調整しなければならない 入力タイプ修飾子(もしあれば)は、これら指定されています「」、内[
及び]
配列型導出。キーワードstatic
が[
と]
配列型の派生にも含まれている場合、関数の呼び出しごとに、対応する実引数の値により、少なくともサイズで指定された数の要素を持つ配列の最初の要素へのアクセスが提供されます。式。
だから、要するに、任意の関数のパラメータは、として宣言T a[]
またはT a[N]
扱われているかのように、それが宣言されましたT *a
。
では、なぜ配列パラメーターがポインターとして宣言されているかのように扱われるのですか?理由は次のとおりです。
6.3.2.1左辺値、配列、および関数指定子
...
3sizeof
演算子または単項&
演算子のオペランドである場合、または配列を初期化するために使用される文字列リテラル、型が「型の配列」の式を除く『へのポインタ」型の表現に変換され、』型配列オブジェクトの最初の要素を指すとは左辺値ではないことを'。配列オブジェクトにレジスタストレージクラスがある場合、動作は未定義です。
次のコードがあるとします:
int main(void)
{
int arr[10];
foo(arr);
...
}
への呼び出しでfoo
は、配列式arr
はsizeof
orのオペランドではない&
ため、その型は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つのバージョンに違いはありません。少なくともコンパイラはそれらを等しく脅かします。