아침에 잠깐 이것 저것 살펴보던 중에 gcc(GCC == GNU compiler collection) 컴파일 warning 중에 흥미로운 게 하나 있어서 내용을 정리해본다.
만약 예를 들어 아래와 같은 코드가 있다고 가정하자.
#include<signal.h> // signal()
#include<stdlib.h>
#include<stdio.h>
// #include<unistd.h> // pause()
static void signal_handler(int signo) {
if (signo == SIGINT) {
fprintf(stdout, "Caught SIGINT!\n");
} else if (signo == SIGTERM) {
fprintf(stdout, "Caught SIGTERM!\n");
} else {
fprintf(stderr, "Unexpected signal!\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
int main(void) {
if (signal(SIGINT, signal_handler) == SIG_ERR) {
fprintf(stderr, "Cannot handle SIGINT!\n");
exit(EXIT_FAILURE);
}
if (signal(SIGTERM, signal_handler) == SIG_ERR) {
fprintf(stderr, "Cannot handle SIGTERM!\n");
exit(EXIT_FAILURE);
}
if (signal(SIGPROF, signal_handler) == SIG_ERR) {
fprintf(stderr, "Cannot handle SIGPROF\n");
exit(EXIT_FAILURE);
}
for(;;) {
pause();
}
return 0;
}
pause() 콜을 쓰려면 반드시 unistd.h를 include해야한다. 하지만 주석 처리되어있는 상태에서 이걸 아래 명령어로 컴파일 시킨다면?
gcc -Wall -Wextra -O2 -g -o sigterm_and_sigint_handler sigterm_and_sigint_handler.c
아래처럼 warning이 나온다.

왜 필요한 헤더가 없는데도 warning 수준에서만 그치고, 실제 실행파일은 생성될까?
결론부터 말하면 컴파일 자체가 성공하는 이유는 컴파일러가 pause()가 그냥 존재한다고 스스로 가정하고 넘어가기 때문이다.
즉 함수 선언이 없으면 int pause()라고 암묵적으로 선언했다고 가정하는 것이다. 이걸 implicit declaration이라고 부르고, C89규칙이라고 한다. 그런데 C99부터는 표준에서 이게 삭제되었으나, GCC에서는 예전 코드때문에 호환성 때문에 이렇게 남겨두었다고 한다.
그래서 이걸 err수준으로 취급하려면 아래처럼 gcc 옵션을 추가해주면 된다.
gcc -Wall -Wextra -Werror=implicit-function-declaration -O2 -g -o sigterm_and_sigint_handler sigterm_and_sigint_handler.c
이렇게 하면 error수준이 되어 실행 파일이 나오지 않는다.
참고로 implicit declaration은 컴파일러가 임의로 함수가 있다고 선언을 정의하기 때문에 꽤나 위험한 문제가 생길 수 있다고 한다. 실제 내가 원했던 콜과 정의가 살짝 다른 함수가 컴파일러에 의해 암묵적으로 선언되면 문제가 발생할 수 있다고 한다.
그러니 이러한 컴파일 옵션은 항상 빡빡하게 켜두는게 좋은 것 같다.
'C & C++ > C' 카테고리의 다른 글
| [C] What is #define _XOPEN_SOURCE 600 ?? (0) | 2026.01.24 |
|---|---|
| [C] C에서 자주 쓰이는 libc 핵심 함수 모음 (0) | 2026.01.13 |
| [C] DMA란 무엇인가? (0) | 2026.01.12 |
| [C] stdio buffering control (0) | 2026.01.11 |
| [C] 일반적인 에러 상황에서 어떤 값을 리턴하는 게 좋을까? (0) | 2026.01.11 |