매개 변수가없는 함수 (실제 함수 정의와 비교하여)가 컴파일되는 이유는 무엇입니까?


388

방금 컴파일하는 이유에 대해 혼란스러워하는 누군가의 C 코드를 발견했습니다. 이해할 수없는 두 가지 사항이 있습니다.

첫째, 함수 프로토 타입에는 실제 함수 정의에 비해 매개 변수가 없습니다. 둘째, 함수 정의의 매개 변수에 유형이 없습니다.

#include <stdio.h>

int func();

int func(param)
{
    return param;
}

int main()
{
    int bla = func(10);    
    printf("%d", bla);
}

왜 이것이 작동합니까? 두 개의 컴파일러에서 테스트했으며 정상적으로 작동합니다.


77
K & R C입니다. 전체 기능 프로토 타입이 나오기 전인 1980 년대에 이와 같은 코드를 작성했습니다.
hughdbrown

6
GCC는 함께 경고 않는 -Wstrict-prototypes양에 int func()int main()XC : 3 : 경고 : 함수 선언 프로토 타입이 아니다. 당신은 선언해야 main()로서 main(void)뿐만 아니라.
Jens

3
@Jens 왜 질문을 편집 했습니까? 당신은 요점을 놓친 것 같습니다 ...
dlras2

1
방금 암시 적 int를 명시 적으로 만들었습니다. 그것이 요점을 어떻게 그리워합니까? 요점은와 int func();호환되는 이유라고 생각합니다 int func(arglist) { ... }.
Jens

3
@MatsPetersson 잘못되었습니다. C99 5.1.2.2.1은 귀하의 주장과 명백히 모순되며 인수가없는 버전은 int main(void)입니다.
Jens

답변:


271

다른 모든 답변은 정확하지만 완료를 위해

함수는 다음과 같은 방식으로 선언됩니다.

  return-type function-name(parameter-list,...) { body... }

return-type 은 함수가 반환하는 변수 유형입니다. 배열 유형 또는 함수 유형이 될 수 없습니다. 지정하지 않으면 int로 간주 됩니다.

function-name함수의 이름 입니다.

parameter-list 는 함수가 쉼표로 구분하여 취하는 매개 변수 목록입니다. 매개 변수가 제공되지 않으면 함수는 사용하지 않으며 빈 괄호 세트 또는 키워드 void를 사용하여 정의해야합니다. 매개 변수 목록에서 변수 앞에 변수 유형이 없으면 int로 간주 됩니다. 배열과 함수는 함수로 전달되지 않지만 포인터로 자동 변환됩니다. 줄임표 (, ...)로 목록이 종료되면 설정된 수의 매개 변수가 없습니다. 참고 : 줄임표를 사용할 때 stdarg.h 헤더를 사용하여 인수에 액세스 할 수 있습니다.

그리고 완전성을 위해 다시. C11 사양 6 : 11 : 6부터 (페이지 : 179)

빈 괄호 (프로토 타입 형식 매개 변수 유형 선언자가 아님) 와 함께 함수 선언자를 사용하는 것은 더 이상 사용 되지 않는 기능입니다 입니다.


2
"매개 변수 목록에서 변수 앞에 변수 유형이 없으면 int로 가정됩니다." 나는 당신이 제공 한 링크에서 이것을 보았지만 표준 c89, c99에서 찾을 수 없습니다 ... 다른 소스를 제공 할 수 있습니까?
godaygo

"매개 변수를 지정하지 않으면이 함수는 아무 것도 취하지 않으며 빈 괄호로 정의해야합니다." Tony The Lion의 답변과 모순되는 것처럼 들립니다 .
jakun

160

C func()에서 당신은 많은 수의 인수를 전달할 수 있음을 의미합니다. 인수를 원하지 않으면로 선언해야 func(void)합니다. 지정되지 않은 경우 함수에 전달하는 유형의 기본값은 int입니다.


3
실제로 단일 유형의 기본값은 int입니다. 실제로 모든 인수는 기본적으로 int (반환 유형)로 설정됩니다. 호출해도 괜찮습니다 func(42,0x42);( 모든 호출은 두 개의 인수 형식을 사용해야합니다).
Jens

1
C에서는 변수를 선언 할 때와 같이 지정되지 않은 유형이 기본값으로 int 로 설정되는 것이 일반적입니다 . unsigned x; 변수 x는 어떤 유형입니까? 그것은 부호없는 int로
bitek

4
오히려 옛날에는 암시 적 int 일반적 이라고 말하고 싶습니다 . 확실히 더 이상은 아니며 C99에서는 C에서 제거되었습니다.
Jens

2
이 답변은 빈 매개 변수 목록이있는 함수 프로토 타입에 적용되지만 빈 매개 변수 목록이있는 함수 정의 에는 적용 되지 않습니다 .
자폐증

"mnemonicflow는"unspecified type int to int "가 아닙니다. unsigned와 같은 유형의 다른 이름 일뿐 unsigned int입니다.
hobbs

58

int func();C 표준이없는 날, 즉 K & R C 의 날부터 폐기 된 함수 선언 (1989 년 이전에 첫 번째 "ANSI C"표준이 출판 된 해) .

K & R C에는 프로토 타입없었 으며 키워드 void는 아직 발명되지 않았습니다. 컴파일러에게 함수 의 리턴 유형 에 대해 알리는 것만으로도 충분 합니다. K & R C의 빈 매개 변수 목록은 "지정되지 않았지만 고정 된"수의 인수를 의미합니다. 당신이 가진 함수를 호출해야 함을 의미합니다 고정 같은 A와 반대 (인수의 수마다 가변 인자 와 같은 기능을printf 수와 종류는 각각의 호출에 따라 다를 수 있습니다).

많은 컴파일러가이 구문을 진단합니다. 특히 gcc -Wstrict-prototypes"함수 선언은 프로토 타입이 아닙니다"라고 말할 것입니다. 처럼 (특히 C ++에 중독 된 경우) 그렇지 않습니다. 구식 K & R C 반환 형식 선언입니다.

경험 법칙 : 빈 매개 변수 목록 선언을 비워 두지 마십시오 int func(void). 구체적으로 사용하십시오. 이것은 K & R 리턴 타입 선언을 적절한 C89 프로토 타입으로 바꿉니다. 컴파일러는 행복하고 개발자는 행복하며 정적 검사기는 행복합니다. C ++의 ^ W ^ Wfond가 오해 한 사람들은 외국 언어 기술을 연습하려고 할 때 추가 문자를 입력해야하기 때문에 울 수도 있습니다.


1
이것을 C ++과 관련시키지 마십시오. 그것은이다 상식 빈 인수 목록이 의미 없다고 매개 변수를 , 그리고 세계의 사람들은 40 년 전 K & R 사람에 의해 실수를 다루는 관심이 없습니다. 그러나위원회 전문가들은 C99, C11로 피상적 인 선택을 계속하고 있습니다.
pfalcon

1
@pfalcon 상식 은 매우 주관적입니다. 한 사람에게 상식적인 것은 다른 사람에게는 명백한 광기입니다. 전문 C 프로그래머는 빈 매개 변수 목록이 지정되지 않았지만 고정 된 수의 인수를 나타냅니다. 변경하면 많은 구현이 중단 될 수 있습니다. 자동 변경을 피하기위한 비난위원회가 잘못된 나무 IMOM을 짖고 있습니다.
Jens

53
  • 빈 매개 변수 목록은 "모든 인수"를 의미하므로 정의가 잘못되지 않았습니다.
  • 누락 된 유형은로 가정됩니다 int.

나는 이것을 통과하는 모든 빌드가 구성된 경고 / 오류 수준에서 부족하다고 생각할 것이지만 실제 코드를 허용하는 데는 아무런 의미가 없습니다.


30

그것은의 K & R 스타일 함수 선언 및 정의. C99 표준 (ISO / IEC 9899 : TC3)에서

섹션 6.7.5.3 함수 선언자 (시제품 포함)

식별자 목록은 함수 매개 변수의 식별자 만 선언합니다. 해당 함수 정의의 일부인 함수 선언자의 빈 목록은 함수에 매개 변수가 없음을 지정합니다. 해당 함수의 정의에 포함되지 않은 함수 선언자의 빈 목록은 매개 변수의 수 또는 유형에 대한 정보가 제공되지 않도록 지정합니다. 두 함수 유형이 모두 "이전 스타일"인 경우 매개 변수 유형이 비교되지 않습니다.

6.11.6 함수 선언자

빈 괄호 (프로토 타입 형식 매개 변수 유형 선언자가 아님)와 함께 함수 선언자를 사용하는 것은 쓸모없는 기능입니다.

6.11.7 절 정의

별도의 매개 변수 식별자 및 선언 목록 (프로토 타입 형식 매개 변수 유형 및 식별자 선언자가 아님)과 함께 함수 정의를 사용하는 것은 더 이상 사용되지 않는 기능입니다.

예전 스타일은 K & R을 의미합니다 스타일을

예:

선언: int old_style();

정의:

int old_style(a, b)
    int a; 
    int b;
{
     /* something to do */
}

16

C는 int함수 반환 유형 및 매개 변수 목록에 유형이 지정되지 않은 것으로 가정 합니다. . 이 규칙에 대해서만 이상한 일이 가능합니다.

함수 정의는 다음과 같습니다.

int func(int param) { /* body */}

프로토 타입이라면

int func(int param);

프로토 타입에서는 매개 변수 유형 만 지정할 수 있습니다. 매개 변수 이름은 필수가 아닙니다. 그래서

int func(int);

또한 매개 변수 유형을 지정하지 않지만 이름 int은 유형으로 간주됩니다.

int func(param);

더 멀리 가면 다음도 작동합니다.

func();

컴파일러는 int func()쓸 때를 가정합니다 func(). 그러나 func()함수 본문 안에 넣지 마십시오 . 그것은 함수 호출입니다


3
빈 매개 변수 목록은 암시 적 int 유형과 관련이 없습니다. int func()의 암시 적 형식이 아닙니다 int func(int).
존 Bartholomew

11

@Krishnabhadra에서 언급했듯이 다른 사용자의 모든 이전 응답은 올바르게 해석되었으며 일부 요점을보다 자세하게 분석하려고합니다.

ANSI-C에서와 같이 Old-C에서 " untyped formal parameter "는 8 비트 MPU에서 작업 레지스터 또는 명령어 깊이 기능 (그림자 레지스터 또는 명령어 누적주기)의 차원을 취하면 16 비트에서 int16이됩니다. 64 비트 아키텍처가 다음과 같은 옵션을 컴파일하도록 선택할 수 있습니다.

높은 수준에서 구현이 더 단순 해 보이지만 여러 매개 변수를 전달하려면 제어 차원 데이터 유형 단계에서 프로그래머의 작업이 더 까다로워집니다.

다른 경우, 일부 마이크로 프로세서 아키텍처의 경우, 사용자 정의 된 ANSI 컴파일러는이 오래된 기능 중 일부를 사용하여 코드 사용을 최적화하여 이러한 "유형화되지 않은 형식 매개 변수"의 위치를 ​​작업 레지스터 내부 또는 외부에서 작동하도록합니다. "volatile"및 "register"를 사용하는 것과 거의 동일합니다.

그러나 가장 현대적인 컴파일러는 두 가지 유형의 매개 변수 선언을 구분하지 않습니다.

리눅스에서 gcc를 사용한 컴파일의 예 :

main.c

main2.c

main3.c  
어쨌든이 프로토 타입에 대한 매개 변수가없는 호출이 없기 때문에 프로토 타입의 로컬 문장은 사용되지 않습니다. "비정형 형식 매개 변수"와 함께 시스템을 외부 호출에 사용하는 경우 선언적 프로토 타입 데이터 유형 생성을 진행하십시오.

이처럼 :

int myfunc(int param);

5
이미지의 크기 (바이트)가 최소가되도록 이미지를 최적화하는 데 어려움을 겪었습니다. 그림이 생성 된 코드의 차이가 없다는 것을 빨리 볼 수 있다고 생각합니다. 코드를 테스트하여 문제에 대한 이론화뿐만 아니라 주장하는 내용에 대한 실제 문서를 생성했습니다. 그러나 모두가 세상을 같은 방식으로 보는 것은 아닙니다. 커뮤니티에 더 많은 정보를 제공하려는 시도가 너무 귀찮게해서 정말 유감입니다.
RTOSkit

1
지정되지 않은 반환 유형이 항상 int이며, 일반적으로 작업 레지스터 크기 또는 16 비트 중 작은 값입니다.
supercat

@supercat +1 완벽한 공제, 당신 말이 맞아요! 이것은 위에서 언급 한 것입니다. 특정 아키텍처를 위해 기본적으로 설계된 컴파일러는 항상 문제의 CPU / MPU의 작업 레지스터의 크기를 반영하므로 임베디드 환경에는 다양한 재 타이핑 전략이 있습니다. 실시간 OS, 컴파일러 계층 (stdint.h) 또는 이식성 계층으로 이동하여 다른 많은 구조에서 동일한 OS를 이식 가능하게하며 CPU / MPU 특정 유형 (int, long, long long 등)을 정렬하여 얻습니다. 일반 시스템 유형 u8, u16, u32
RTOSkit

@supercat 운영 체제 나 특정 컴파일러에서 제공하는 제어 유형이 없으면 개발 시간에 약간의 감수성이 필요하며, 모든 "유형화되지 않은 할당"이 응용 프로그램 디자인과 일치하도록하여 " "32bit int"가 아닌 16bit int "
RTOSkit

@ RTOSkit : 귀하의 답변에 따르면 8 비트 프로세서의 기본 유형은 Int8입니다. 필자는 PICmicro 브랜드 아키텍처를위한 몇 가지 C-ish 컴파일러를 회상하지만, C 표준과 비슷한 어떤 것도 int범위의 모든 값을 보유 할 수없는 유형을 허용하지 않았다고 생각 합니다. 32767 ~ +32767 (-32768은 필요하지 않음) 및 모두 보유 가능char 값 있습니다 ( char16 비트 인 경우 부호가 있거나 int더 커야 함을 의미 함).
supercat

5

매개 변수 유형과 관련하여 이미 올바른 답변이 있지만 컴파일러에서 듣고 싶다면 플래그를 추가 할 수 있습니다 (플래그는 거의 항상 좋은 생각입니다).

gcc foo.c -Wextra내가 사용하여 프로그램을 컴파일 :

foo.c: In function func’:
foo.c:5:5: warning: type of param defaults to int [-Wmissing-parameter-type]

이상하게도 -Wextra이것을 포착하지 못합니다 clang( -Wmissing-parameter-type어떤 이유로 든, 아마도 위에서 언급 한 역사적인 것들을 인식하지 못합니다 ) -pedantic.

foo.c:5:10: warning: parameter 'param' was not declared, 
defaulting to type 'int' [-pedantic]
int func(param)
         ^
1 warning generated.

그리고 위에서 int func()언급 한 것처럼 프로토 타입 문제 int func(void)는 예상대로 오류를 발생시키는 것으로 명시 적으로 정의하지 않는 한 임의의 매개 변수를 말합니다 .

foo.c: In function func’:
foo.c:6:1: error: number of arguments doesnt match prototype
foo.c:3:5: error: prototype declaration
foo.c: In function main’:
foo.c:12:5: error: too many arguments to function func
foo.c:5:5: note: declared here

또는 clang 같은 :

foo.c:5:5: error: conflicting types for 'func'
int func(param)
    ^
foo.c:3:5: note: previous declaration is here
int func(void);
    ^
foo.c:12:20: error: too many arguments to function call, expected 0, have 1
    int bla = func(10);
              ~~~~ ^~
foo.c:3:1: note: 'func' declared here
int func(void);
^
2 errors generated.

3

함수 선언에 매개 변수가없는 경우 (즉, 비어있는 경우) 지정되지 않은 수의 인수를 사용합니다. 인수를 취하지 않으려면 다음으로 변경하십시오.

int func(void);

1
"함수 프로토 타입에 매개 변수가없는 경우" 이것은 함수 프로토 타입이 아니라 함수 선언 일뿐입니다.
effeffe

@effeffe는 그것을 고쳤다. 나는이 질문이 많은 주목을받을 것이라는 것을 몰랐다;)
PP

3
"함수 프로토 타입"은 "함수 선언"에 대한 대체 (구식) 표현이 아닌가?
Giorgio

@Giorgio IMO, 둘 다 맞습니다. "함수 프로토 타입"은 "함수 정의"와 일치해야합니다. 아마도 effeffe가 질문에서 선언을 의미한다고 생각하고 내 의미는 내 대답에 있다고 생각합니다.
PP

@Giorgio 아니오, 함수 선언은 반환 유형 값, 식별자 및 선택적 매개 변수 목록으로 이루어집니다. 함수 프로토 타입은 매개 변수 목록이있는 함수 선언입니다.
effeffe

0

이것이 일반적으로 사람들에게 코드를 컴파일하도록 조언하는 이유입니다.

cc -Wmissing-variable-declarations -Wstrict-variable-declarations -Wold-style-definition

이 플래그는 몇 가지 사항을 적용합니다.

  • -Wmissing-variable-declarations : 프로토 타입을 먼저 얻지 않고 비 정적 함수를 선언하는 것은 불가능합니다. 따라서 헤더 파일의 프로토 타입이 실제 정의와 일치 할 가능성이 높습니다. 또는 공개적으로 볼 필요가없는 함수에 static 키워드를 추가하도록합니다.
  • -가변 변수 선언 : 프로토 타입이 인수를 올바르게 나열해야합니다.
  • -Wold-style-definition : 함수 정의 자체도 인수를 올바르게 나열해야합니다.

이 플래그는 기본적으로 많은 오픈 소스 프로젝트에서 사용됩니다. 예를 들어, FreeBSD는 Makefile에서 WARNS = 6으로 빌드 할 때이 플래그들을 활성화합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.