배열 매개 변수의 크기가 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
Minor nit : sizeof연산자는 유형의 객체를 반환 size_t하므로 %zu(C99)로 인쇄 하거나 호출 에서 위와 같이 int사용 하는 경우 캐스트 %d해야 printf합니다.
Alok Singhal

4
Alok의 진술이 정확합니다. printf (..)에서 잘못된 형식 지정자를 사용하는 것은 UB입니다.
Prasoon Saurav

1
@ Chris_45 : C에는 참조가 없지만 C에서는 다음과 같이 전체 배열에 대한 포인터로 배열을 전달할 수 있습니다 void PrintSize(int (*p_someArray)[10]). 은 함수 내에서 당신은 참조 연산자를 사용하여 배열에 액세스 할 수 있습니다 *: sizeof(*p_someArray). 이것은 C ++에서 참조를 사용하는 것과 동일한 효과를 갖습니다.
AnT

18

포인터이기 때문에 배열의 크기를 함수에 두 번째 매개 변수로 전달하는 것이 일반적인 구현입니다.


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

따라서 배열의 길이를 두 번째 매개 변수로 전달해야합니다. 둘 다 상수 크기의 배열을 선언하고 나중에 해당 배열을 함수에 전달하는 코드를 작성할 때 배열 길이 상수가 코드의 여러 위치에 표시되는 것은 고통 스럽습니다.

구조를위한 K & R :

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

이제 다음과 같이 할 수 있습니다.

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

배열의 크기를 코딩 시간에 사용할 수 없지만 런타임에만 사용할 수 있다면 어떻게 될까요? 크기를 하드 코딩하지 않고 배열의 크기를 계산하는 다른 방법이 있습니까?
weefwefwqg3

표시된 메서드는 배열 선언이 "in view"인 경우에만 작동합니다. 다른 모든 경우에는 배열 길이를 수동으로 전달해야합니다.
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는 네 가지 경우 모두에서 int에 대한 포인터가됩니다. func에 배열을 전달하면 즉시 첫 번째 요소에 대한 포인터로 붕괴됩니다. (64 비트 시스템에서 64 비트 포인터는 32 비트 int의 두 배이므로 크기 비율은 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 질문에 어떻게 도움이 될까요?
alk

3

C 언어에서는 알 수없는 배열의 크기를 결정하는 방법이 없으므로 수량은 첫 번째 요소에 대한 포인터와 함께 전달되어야합니다.


2
일반적으로 배열의 크기 (요소 수)를 배열과 함께 함수에 전달해야합니다 (예 : char[]문자열 배열 의 끝에서 널 문자 종료 자 ).
David R Tribble

" 알 수없는 배열 "이 무엇입니까?
alk

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에는 " 참조에 의한 호출 "이 없습니다 .
alk

" 배열의 크기를 인쇄하면 첫 번째 요소의 크기를 인쇄합니다. "아니오, 포인터의 크기를 인쇄합니다.
alk

-1

'C'프로그래밍 언어에서 '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 바이트이 경우 하나의 배열에 10 개의 정수가 선언되어 결과는 '10 * sizeof ( int) '.

몇 가지 팁 :

이와 같이 선언 된 배열이 있다면 'int n [] = {1, 2, 3, ... 155 ..};'. 그래서 우리는이 배열에 얼마나 많은 요소가 저장되어 있는지 알고 싶습니다. 이 알고리즘을 사용하십시오.

sizeof (배열 _ 이름) / sizeof (배열 _ 유형)

코드 : #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 2016 년

-3

배열의 크기는 느슨합니다. 대부분의 경우 배열은 메모리에 대한 포인터입니다. 선언의 크기는 배열에 할당 할 메모리의 양만 컴파일러에게 알려줍니다. 유형과 관련이 없으므로 sizeof ()는 계속 진행할 필요가 없습니다.


3
죄송합니다.이 답변은 잘못된 것입니다. 배열은 "느슨한 크기"도 아니고 "메모리에 대한 포인터"도 아닙니다. 배열은 매우 정확한 크기를 가지며 배열 이름이 첫 번째 요소에 대한 포인터를 나타내는 위치는 C 표준에 의해 정확하게 지정됩니다.
Jens
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.