/* foo4.c */
#include <stdio.h>
void f(void);
int x; // weak
int main()
{
x = 15213;
f();
printf("x = %d\n", x);
return 0;
}
/* bar4.c */
int x; // weak
void f()
{
x = 15212;
}
이 경우 링커는 (규칙 3에 따라)
두 weak 심볼 중 하나를 임의로 선택한다.
→ 결과는 예측 불가능할 수 있으며, 버그를 유발할 수 있다.
/* foo5.c */
#include <stdio.h>
void f(void);
int y = 15212;
int x = 15213;
int main()
{
f();
printf("x = 0x%x y = 0x%x\n", x, y);
return 0;
}
/* bar5.c */
double x;
void f()
{
x = -0.0;
}
x86-64 리눅스에서:
double은 8바이트int는 4바이트주소 배치:
x = 0x601020
y = 0x601024
x = -0.0은 double 값이므로
그 8바이트가 x와 y의 메모리 공간을 모두 덮어쓴다.
결과:
linux> gcc -Wall -Og -o foobar5 foo5.c bar5.c
/usr/bin/ld: Warning: alignment 4 of symbol ‘x’ ... is smaller than 8
linux> ./foobar5
x = 0x0 y = 0x80000000
→ x는 0으로, y는 double 음수 0의 하위 비트가 덮여 0x80000000이 됨.
이런 오류는 링커가 오류가 아닌 경고만 출력하기 때문에
발견이 어렵고, 나중에 프로그램 실행 중 전혀 엉뚱한 시점에 나타날 수 있다.
대규모 시스템에서는 이런 종류의 버그를 찾기가 매우 어렵다.
대부분의 프로그래머는 링커 동작에 익숙하지 않아
경고를 무시하기 때문이다.
💡 팁:
GCC 옵션-fno-common을 사용하면
중복 전역 심볼 정의 시 오류를 발생시킨다.
또는-Werror옵션으로 모든 경고를 오류로 취급할 수도 있다.