C에서 배열과 배열 포인터를 함수에 전달하는 것의 차이점


110

C에서 두 기능의 차이점은 무엇입니까?

void f1(double a[]) {
   //...
}

void f2(double *a) {
   //...
}

실질적으로 긴 배열에서 함수를 호출하면이 두 함수가 다르게 동작할까요? 스택에서 더 많은 공간을 차지할까요?

답변:


114

첫째, 몇 가지 표준 :

(원형 포함) 6.7.5.3 함수 선언자
...
'배열'과 같은 파라미터도 7a 선언 유형 '' '으로 규정 포인터'로 조정된다 입력 타입 한정자 (있는 경우) 그 지정된 ', 내 []어레이 형의 유도. 키워드 static가 배열 유형 파생 [및 내부에도 표시되는 경우 ]함수에 대한 각 호출에 대해 해당 실제 인수의 값은 크기에 지정된만큼의 요소가있는 배열의 첫 번째 요소에 대한 액세스를 제공해야합니다. 표현.

그래서, 짧은에, 어떤 함수 매개 변수는 다음과 같이 선언 T a[]또는 T a[N]처리 것처럼 그것을 선언했다 T *a.

그렇다면 배열 매개 변수가 포인터로 선언 된 것처럼 처리되는 이유는 무엇입니까? 그 이유는 다음과 같습니다.

6.3.2.1 Lvalues, 배열 및 기능 부호
...
그것의 오퍼랜드 인 경우를 제외하고 3 sizeof운영자 또는 단항 &연산자, 또는 어레이를 초기화하는 데 이용되는 문자열의 문자 입력 '의 배열을 갖는 표현 유형을 ' 의 '포인터'유형의 표현으로 변환된다 " 배열 개체의 초기 요소를 가리키고는 좌변 아니라고 '. 배열 객체에 레지스터 스토리지 클래스가 있으면 동작이 정의되지 않습니다.

다음 코드가 주어집니다.

int main(void)
{
  int arr[10];
  foo(arr);
  ...
}

에 대한 호출 foo에서 배열 표현식 arrsizeof또는 의 피연산자가 &아니므로 해당 유형은 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)
{
  ...
}

따라서 두 형식은 동일합니다.

6.7.5.3/7의 마지막 문장은 C99와 함께 도입되었으며 기본적으로 다음과 같은 매개 변수 선언이있는 경우

void foo(int a[static 10])
{
  ...
}

에 해당하는 실제 매개 변수 a는 요소가 10 개 이상인 배열이어야합니다 .


1
(적어도 일부 오래된) MSVC C ++ 컴파일러를 사용할 때 차이가 있습니다. 컴파일러가 두 경우에서 함수 이름을 다르게 잘못 조작하여 (그렇지 않으면 동일하다는 것을 인식하면서) 링크 문제가 발생하기 때문입니다. 여기에서 "수정하지 않음"버그 보고서를 참조하십시오. connect.microsoft.com/VisualStudio/feedback/details/326874/…
greggo

29

차이점은 순전히 구문입니다. C에서 함수 매개 변수에 배열 표기법을 사용하면 자동으로 포인터 선언으로 변환됩니다.


1
@Kaushik :이 경우에는 동일하지만 일반적인 경우
BlueRaja-Danny Pflughoeft 2011

@BlueRaja : 예, C의 함정 중 하나입니다. 함수 매개 변수 의 선언은 지역 변수의 선언 과 매우 유사 하지만 몇 가지 미묘한 차이 (예 :이 배열-포인터 자동 변환)가 있습니다. 부주의 한 프로그래머를 물기 쉽습니다.
Thomas Pornin 2011

0

아니요, 그들 사이에는 차이가 없습니다. 테스트를 위해 Dev C ++ (mingw) 컴파일러에서이 C 코드를 작성했습니다.

#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

따라서이 호출의 두 버전 사이에는 차이가 없습니다. 적어도 컴파일러는 이들을 동등하게 위협합니다.


18
미안하지만 이것은 gcc의 일부 버전이 x86에서 둘 다에 대해 동일한 어셈블리를 생성한다는 것을 증명할뿐입니다. 정답, 잘못된 설명.
lambdapower
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.