compiler-errors - 왜 gcc는 인수가 없도록 정의 된 함수에 인수를 전달할 수 있습니까?




(5)

레거시 이유로, 매개 변수 목록에 대해 () 를 사용하여 함수를 선언한다는 것은 "함수가 호출 될 때 매개 변수를 찾아내는"것을 의미합니다. 함수에 매개 변수가 없도록 지정하려면 (void) .

편집 : 나는이 문제에 대한 평판이 오래되었다고 생각합니다. 그냥 아이들이 어떤 프로그래밍을 사용했는지 알고 있습니다. 여기 제 첫 번째 프로그램이 있습니다. (C가 아닌, 이전에 작업 한 내용을 보여줍니다.)

나는 왜이 코드를 컴파일하지 않는가?

#include <stdio.h>
void foo() {
    printf("Hello\n");
}

int main() {
    const char *str = "bar";
    foo(str);
    return 0;
}

gcc는 foo ()에 너무 많은 인수를 전달한다는 경고를 던지지 않습니다. 이 예상되는 동작입니까?


C99 표준 (6.7.5.3) 및 C11 표준 (6.7.6.3) 상태 :

식별자 목록은 함수 매개 변수의 식별자 만 선언합니다. 해당 함수 정의의 일부인 함수 선언자의 빈 목록은 함수에 매개 변수가 없음을 나타냅니다. 해당 함수 정의의 일부가 아닌 함수 선언자의 빈 목록은 매개 변수의 수 또는 유형에 대한 정보가 제공되지 않음을 지정합니다.

foo의 선언은 정의의 일부이기 때문에 선언은 foo가 0 개의 인수를 취하도록 지정하므로 foo (str) 호출은 적어도 도덕적으로 잘못되었습니다. 그러나 아래에서 설명하는 것처럼 C에는 "잘못된"정도가 다르며 컴파일러는 특정 종류의 "잘못된"문제를 처리하는 방법이 다를 수 있습니다.

약간 더 간단한 예제를 사용하려면 다음 프로그램을 고려하십시오.

int f() { return 9; }
int main() {
  return f(1);
}

만약 내가 위의 Clang을 사용하여 컴파일 :

tmp$ cc tmp3.c
tmp3.c:4:13: warning: too many arguments in call to 'f'
  return f(1);
         ~  ^
1 warning generated.

gcc 4.8로 컴파일하면 -Wall을 사용하더라도 오류나 경고가 발생하지 않습니다. 이전 답변은 -Wstrict-prototype을 사용하여 제안했는데, 이는 f의 정의가 프로토 타입 형식이 아니라고 정확하게보고하지만 실제로는 그렇지 않습니다. C 표준은 위와 같은 프로토 타입이 아닌 형식의 함수 정의를 허용하며 표준은이 정의가 함수가 0 개의 인수를 취하도록 지정합니다.

이제 제약 조건이 있습니다 (C11 Sec. 6.5.2.2).

호출 된 함수를 나타내는식이 프로토 타입을 포함하는 형식 인 경우 인수의 수는 매개 변수의 수와 일치해야합니다.

그러나 함수의 유형에 프로토 타입이 포함되어 있지 않으므로이 제약 조건은 적용되지 않습니다. 그러나 의미 영역 ( "제약 조건"이 아닌)에 다음 문장이 적용됩니다.

호출 된 함수를 나타내는식이 프로토 타입을 포함하지 않는 형식 인 경우 ... 인수의 수가 매개 변수의 수와 같지 않으면 동작은 정의되지 않습니다.

따라서 함수 호출은 정의되지 않은 동작을 발생시킵니다 (즉, 프로그램이 "엄격하게 준수"하지 않음). 그러나이 표준은 실제 제약이 위반 될 때 진단 메시지를보고하기위한 구현만을 요구하며,이 경우 제약에 대한 위반은 없다. 따라서 gcc는 "준수 구현"을 위해 오류나 경고를보고 할 필요가 없습니다.

그래서 나는 질문에 대한 대답, 왜 gcc는 그것을 허락합니까?, 이것은 제약 위반이 아니기 때문에 gcc가 아무것도보고하지 않아도된다는 것입니다. 게다가 gcc는 -Wall 또는 -Wpedantic을 사용하더라도 모든 종류의 정의되지 않은 동작을보고한다고 주장하지 않습니다. 이것은 정의되지 않은 동작입니다. 즉, 구현이이를 처리하는 방법을 선택할 수 있으며 gcc는 경고없이 컴파일하도록 선택했습니다 (그리고 명백히 인수를 무시합니다).


void foo() {
    printf("Hello\n");
}

foo(str);

C에서이 코드는 제약 조건을 위반하지 않습니다 (프로토 타입 형태로 void foo(void) {/*...*/} 로 정의 된 경우), 제약 조건 위반이 없으므로 컴파일러는 진단을 내리는 데 필요합니다.

그러나이 프로그램은 다음 C 규칙에 따라 정의되지 않은 동작을합니다.

에서:

(C99, 6.9.1p7) "선언자가 매개 변수 유형 목록을 포함하는 경우이 목록은 모든 매개 변수의 유형도 지정하며 이러한 선언자는 나중에 동일한 번역 단위에서 동일한 함수에 대한 이후 호출을위한 함수 프로토 타입 역할을합니다. 선언자가 식별자 목록을 포함하면 142) 매개 변수의 유형은 다음 선언 목록에 선언되어야한다. "

foo 함수는 프로토 타입을 제공하지 않습니다.

에서:

(C99, 6.5.2.2p6) "호출 된 함수를 나타내는식이 프로토 타입을 포함하지 않는 형식 인 경우 [...] 인수의 수가 매개 변수의 수와 같지 않으면 동작이 정의되지 않습니다."

foo(str) 함수 호출은 정의되지 않은 동작입니다.

C는 정의되지 않은 동작을 호출하는 프로그램에 대한 진단을 실행하도록 구현을 위임하지 않지만 프로그램은 여전히 ​​잘못된 프로그램입니다.


C에서 빈 매개 변수 목록으로 선언 된 함수는 호출 될 때 임의의 수의 인수를 허용합니다.이 인수는 일반적인 산술 승격에 종속됩니다. 제공되는 인수가 함수 정의에 적합한 지 확인하는 것은 호출자의 책임입니다.

인수가 0 인 함수를 선언하려면 void foo(void); 를 작성해야합니다 void foo(void); .

이것은 역사적인 이유로; 원래 C 함수는 프로토 타입이 없었습니다. C는 타입이없는 언어 인 B 에서 진화했습니다. 프로토 타입이 추가되었을 때 원래의 타입이없는 선언은 이전 버전과의 호환성을 위해 언어에 남아있었습니다.

gcc가 빈 매개 변수 목록에 대해 경고하도록하려면 -Wstrict-prototypes 사용 -Wstrict-prototypes .

인수 유형을 지정하지 않고 함수가 선언되거나 정의되면 경고합니다. (인수 유형을 지정하는 선언이 선행 될 경우 경고없이 이전 스타일의 함수 정의가 허용됩니다.)


extern "C" 는 함수 유형을 수정한다는 점에 유의해야합니다. 낮은 수준의 항목을 수정할뿐만 아니라

extern "C" typedef void (*function_ptr_t)();

void foo();

int main() { function_ptr_t fptr = &foo; } // error!

&foo 유형은 typedef가 지정하는 유형과 동일하지 않습니다 (코드는 일부 컴파일러가 아닌 일부 컴파일러에서 허용되지만).





c gcc compiler-errors