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


104

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

#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));
}

回答:


103

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

そう、

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*)


1
C ++では、あなたは、関数への参照によって配列を渡すことができますが、C言語でそれを行うことはできません
Prasoon Saurav

13
配列のサイズを別のパラメーターとして渡す必要があります。次に、配列のサイズはsizeof(* p_someArray)* length
Aric TenEyck

13
マイナーnit:sizeof演算子はのタイプのオブジェクトを返すsize_tため、%zu(C99)で印刷するか、上記のように呼び出しでint使用する場合はキャストする必要があり%dますprintf
Alok Singhal、

4
アロクの発言は正しい。printf(..)で誤ったフォーマット指定子を使用すると、UBになります。
Prasoon Saurav、2009

1
@ Chris_45:Cには参照がありませんが、Cでは、次のように配列全体を指すポインターによって配列を渡すことができますvoid PrintSize(int (*p_someArray)[10])。関数の内部で、あなたは間接参照演算子を使用して配列にアクセスすることができます*sizeof(*p_someArray)。これは、C ++で参照を使用するのと同じ効果があります。
AnT 2009

18

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


16

他の人が述べたように、配列は関数パラメーターとして使用されると、最初の要素へのポインターに減衰します。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] ) );
}

12

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

救助へのK&R:

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

だから今、あなたは行うことができます:

int a[10];
...
myfunction(a, N_ELEMENTS(a));

配列のサイズがコーディング時に利用できず、実行時にのみ利用できる場合はどうなりますか?サイズをハードコーディングせずに配列のサイズを計算する他の方法はありますか?
weefwefwqg3 2017年

示されているメソッドは、配列の宣言が「ビュー内」にある場合にのみ機能します。他のすべてのケースでは、配列の長さを手動で渡す必要があります。
SC Madsen

5

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

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

5

あなたが見つけた振る舞いは、実際には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比率は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 */
}

4

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

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

1
これはCの質問にどのように役立ちますか?
2017

3

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


2
一般に、配列のサイズ(要素数)を配列とともに関数に渡す必要があります。ただし、そのサイズを決定する他の方法(たとえば、char[]文字列配列の最後にあるnull文字ターミネーター)がある場合は除きます。
David R Tribble、

不明な配列」とは何ですか?
2017

3

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

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

#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));
}

0

動作は仕様です。

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

理由は他の回答に記載されています。


0

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

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

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


Cでは「参照による呼び出し」はありません。
2017

" 配列のサイズを出力すると、最初の要素のサイズが出力されます。 "いいえ、ポインタのサイズが出力されます。
alk 2017

-1

'C'プログラミングでは、languange 'sizeof()'が演算子であり、オブジェクトのサイズをバイト単位で返します。'sizeof() '演算子の引数は、左の値の型(整数、浮動小数点数、構造体、配列)でなければなりませんしたがって、配列のサイズをバイト単位で知りたい場合は、非常に簡単に行うことができます。「sizeof()」演算子を使用し、その引数には配列名を使用するだけです。例:

#include <stdio.h>

main(){

 int n[10];
 printf("Size of n is: %d \n", sizeof(n)); 

}

32ビットシステムでの出力は次のようになります:nのサイズは40です。32システムでのinetegerは4バイトであるためです。64xでは8バイトです。この場合、1つの配列で10個の整数が宣言されているため、結果は'10 * sizeof( int) '。

いくつかのヒント:

このように宣言された配列がある場合、「int n [] = {1、2、3、... 155 ..};」です。したがって、この配列に格納されている要素の数を知りたいのです。このアルゴリズムを使用します。

sizeof(name_of_the_array)/ sizeof(array_type)

コード:#include

メイン(){

int n[] = { 1, 2, 3, 44, 6, 7 };
printf("Number of elements: %d \n", sizeof(n) / sizeof(int));
return 0;

}


2
StackOverflowへようこそ。回答を書いていただきありがとうございます。残念ながら、これはsizeof(n)、ローカル変数とsizeof(arg)関数への引数の違いについての質問には対処していませんint[10]
MicroVirus

-3

配列のサイズはゆるいだけです。ほとんどの場合、配列はメモリへのポインタです。宣言のサイズは、配列に割り当てるメモリの量をコンパイラーに指示するだけです。これはタイプに関連付けられていないため、sizeof()は何も実行しません。


3
申し訳ありませんが、この答えは誤解を招くものです。どちらも「ルーズなサイズ」の配列でも「メモリへのポインタ」でもありません。配列のサイズは非常に正確であり、配列名がその最初の要素へのポインターを表す場所は、C標準で正確に指定されています。
イェンス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.