web-dev-qa-db-ja.com

配列パラメータのサイズがメイン内と同じではないのはなぜですか?

パラメータとして送信される配列のサイズがメイン内と同じではないのはなぜですか?

#include <stdio.h>

void PrintSize(int p_someArray[10]);

int main () {
    int myArray[10];
    printf("%d\n", sizeof(myArray)); /* As expected, 40 */
    PrintSize(myArray);/* Prints 4, not 40 */
}

void PrintSize(int p_someArray[10]){
    printf("%d\n", sizeof(p_someArray));
}
99
Chris_45

配列型は暗黙的に関数に渡すときにポインター型に変換されます。

そう、

_void PrintSize(int p_someArray[10]) {
    printf("%zu\n", sizeof(p_someArray));
}
_

そして

_void PrintSize(int *p_someArray) {
    printf("%zu\n", sizeof(p_someArray));
}
_

同等です。したがって、得られるのはsizeof(int*)の値です

99
Prasoon Saurav

これはポインタであるため、関数の2番目のパラメータとして配列のサイズを渡すのが一般的な実装です。

18
user240312

他の人が述べたように、関数パラメーターとして使用される場合、配列は最初の要素へのポインターに減衰します。また、sizeofは式を評価せず、式と共に使用する場合は括弧を必要としないため、パラメーターは実際にはまったく使用されないため、値ではなくタイプでsizeofを記述することもできます。

#include <stdio.h>

void PrintSize1 ( int someArray[][10] );
void PrintSize2 ( int someArray[10] );

int main ()
{
    int myArray[10];
    printf ( "%d\n", sizeof myArray ); /* as expected 40 */
    printf ( "%d\n", sizeof ( int[10] ) ); /* requires parens */
    PrintSize1 ( 0 ); /* prints 40, does not evaluate 0[0] */
    PrintSize2 ( 0 ); /* prints 40, someArray unused */
}

void PrintSize1 ( int someArray[][10] )
{
    printf ( "%d\n", sizeof someArray[0] );
}

void PrintSize2 ( int someArray[10] )
{
    printf ( "%d\n", sizeof ( int[10] ) );
}
16
Pete Kirkham

そのため、2番目のパラメーターとして配列の長さを渡す必要があります。定数サイズの配列を宣言し、後でその配列を関数に渡すコードを作成するとき、配列長定数がコードのいくつかの場所に現れるのは苦痛です...

K&Rによる救助:

#define N_ELEMENTS(array) (sizeof(array)/sizeof((array)[0])) 

だから今、あなたは例えばすることができます:

int a[10];
...
myfunction(a, N_ELEMENTS(a));
12
S.C. Madsen

あなたが見つけた振る舞いは、実際にはC言語の大きないぼです。配列パラメーターを受け取る関数を宣言するたびに、コンパイラーはユーザーを無視し、パラメーターをポインターに変更します。したがって、これらの宣言はすべて最初の宣言のように動作します。

void func(int *a)
void func(int a[])
void func(int a
typedef int array_plz[5];
void func(array_plz a)

aは、4つの場合すべてでintへのポインターになります。 funcに配列を渡すと、すぐに最初の要素へのポインターに減衰します。 (64ビットシステムでは、64ビットポインターは32ビットintの2倍の大きさなので、sizeof ratioは2を返します。)

このルールの唯一の目的は、関数の引数として集計値を渡すことをサポートしていなかった歴史的なコンパイラとの後方互換性を維持することです。

これは、配列を関数に渡すことが不可能であることを意味しません。配列を構造体に埋め込むことで、このいぼを回避できます(これは基本的にC++ 11のstd :: arrayの目的です)。

struct array_rly {
int a[5];
};
void func(struct array_rly a)
{
printf("%zd\n", sizeof(a.a)/sizeof(a.a[0]));  /* prints 5 */
}

または、配列にポインターを渡すことにより:

void func(const int (*a)[5])
{
printf("%zd\n", sizeof(*a)/sizeof((*a)[0]));  /* prints 5 */
}

配列サイズがコンパイル時の定数ではない場合、C99可変長配列で配列へのポインター手法を使用できます。

void func(int n, const int (*a)[n])
{
printf("%zd\n", sizeof(*a)/sizeof((*a)[0]));  /* prints n */
}
5
Walter

配列はパラメーターとして渡されるとポインターに崩壊するためです。これがCの仕組みです。ただし、C++で「配列」を参照で渡し、この問題を克服できます。この関数に異なるサイズの配列を渡すことができることに注意してください。

 // 10 is superfluous here! You can pass an array of different size!
void PrintSize(int p_someArray[10]);
5
AraK

C++では、このまさに目的のために参照によって配列を渡すことができます。

void foo(int (&array)[10])
{
    std::cout << sizeof(array) << "\n";
}
4
Alexandre C.

C言語では、未知の配列のサイズを決定する方法がないため、最初の要素へのポインタと同様に、量を渡す必要があります。

3
user195488

関数に配列を渡すことはできません。

サイズを本当に印刷したい場合は、配列にポインタを渡すことができますが、関数の配列サイズも定義する必要があるため、ポインタはまったく汎用的ではありません。

#include <stdio.h>

void PrintSize(int (*p_anArray)[10]);

int main(void) {
    int myArray[10];
    printf("%d\n", sizeof(myArray)); /* as expected 40 */
    PrintSize(&myArray);/* prints 40 */
}

void PrintSize(int (*p_anArray)[10]){
    printf("%d\n", (int) sizeof(*p_anArray));
}
3
MR.

動作は仕様です。

関数パラメーター宣言の同じ構文は、ローカル変数の定義とはまったく異なることを意味します。

理由は他の回答で説明されています。

0

C言語では、関数に引数として配列を渡すと、自動的にポインターに変換されます。ある関数から渡された配列は、参照による呼び出しとして知られています。これが、呼び出された関数が関数の最初の要素を指すポインターのみを受け取る理由ですこれが理由です

fun(int a [])はfun(int * a)と似ています。

したがって、配列のサイズを印刷すると、最初の要素のサイズが印刷されます。

0
dagrawal