예제에서
printf("char: %d, short: %d, int: %d, long: %d, long long: %d\n",
sizeof(char),
sizeof(short),
sizeof(int),
sizeof(long),
sizeof(long long)
);
라고 되어있는데
long 이랑 long long 을 %ld, %lld 로 바꿔서
long: %ld, long long: %lld 로 컴파일해도 작동되는데
상관없나요?
C 언어는 타입 불일치(type mismatch)에 관대한 언어입니다. 역사적인 이유로 그렇습니다. 타입에 대해 개발자가 매우 잘 이해하고, 매우 조심해서 주의깊에 사용한다는 전제가 깔려 있습니다.
자바, Go, Rust 등 최근에 만들어진 언어로 오면 타입 불일치는 아예 허용하지 않습니다. 즉, 컴파일이 안 된다는 뜻입니다.
개발자의 손에 맡겼더니 수많은 보안 버그를 만들어내는 해악이 더 크기 때문에 프로그래밍 언어 차원에서 더 강력하게 규제하는 게 현재의 추세입니다.
따라서 C 언어를 학습할 때는 책에서 제시한 예제의 범위를 벗어나지 않는 게 좋습니다. 아마도 책에서 제시하는 예제의 범위를 벗어나도 아무 문제없이 대부분은 실행이 될 것이고, 결과도 잘 나올 것입니다. 그 결과...
"이렇게 작성해도 문제가 없군!"
C 언어에 대한 이해도는 대부분 이런 수준입니다. 오랜 경력을 가진 프로그래머나 교수나 C 언어 표준을 제대로 이해하지 못하고 있습니다.
그래서 현업에서 보면 위험한 수준이고 작성하면 안 되는 코드인 경우가 많습니다.
sizeof에 %d를 사용하는 것은 문제가 많은 코드입니다만...
일단 그 맥락을 설명드리자면...
Visual Studio에서 보기 > 오류 목록을 선택해서 오류 목록 창을 표시합니다.
본문의 예제를 빌드해봅니다.
코드 아래에서 오류 목록을 선택하면 경고가 표시되는 것을 알 수 있습니다.
C4477 경고를 보면 %d는 int 형식에 사용해야 하는데, 가변 인수(variadic 인수)의 첫 번째(1의 형식) 항목에 쓰인 건 size_t라는 내용입니다.
인수 5개를 모두 sizeof를 사용하고 있으니 모두 5개의 경고가 표시됩니다.
C6328 경고를 보면 unsigned __int64가 필요하다는 뜻입니다. __int64는 내부적으로 쓰는 것이겠지만, 어쨌거나 unsigned가 쓰여 있으니 비부호이고, __int64이니 64비트 정수가 전달되었다는 뜻입니다. 즉, sizeof 연산자의 결과는 비부호 64비트 정수인데 왜 너는 여기에 int를 쓸 수 있는 %d를 쓰고 있니라는 뜻입니다.
sizeof 연산자가 반환하는 크기는 프로젝트 유형에 따라 달라집니다. 기본값은 x64이고, 64비트 응용프로그램으로 컴파일하기 때문에 64비트 정수라고 하는 것이고, x64 -> x86으로 변경하면 32비트 비부호 정수가 필요하다고 경고를 할 겁니다.
sizeof 연산자의 반환 타입은 size_t입니다.
보통 size_t는 unsigned 정수로 정의되고, 32비트 환경이면 32비트 비부호 정수, 64비트 환경이면 64비트 비부호 정수로 정의됩니다.
만일 sizeof 연산자가 '정수'를 반환한다고 하면 틀린 겁니다. 정확하게는 '비부호 정수'입니다. 크기는 음수가 없고, 표준에서는 unsigned로 정의하기 때문입니다.
C 언어는 50년 이상된 언어이고 당시엔 8비트 프로세서였으니 50년 전이면 sizeof 연산자는 8비트 비부호 정수를 반환했을 겁니다.
C 언어는 하드웨어를 다루는 언어이고, 해당 하드웨어에 따라 크기가 달라집니다. 자바, Go, Rust 등의 현대 언어는 32비트, 64비트 환경을 가정하고 크기를 고정으로 정해두었습니다. 역사가 짧은 언어의 장점이고, 현대 프로그래밍 언어의 장점이죠.
그러면 sizeof가 반환하는 64비트 비부호 정수에 해당하는 서식 지정자는 무엇인가?
Visual Studio에서는 메모리 모델에 따라 long long이 64비트이고, unsigned long long도 64비트입니다.
따라서 sizeof 연산자가 반환하는 비부호 64비트에 맞춘다면 unsigned long long을 출력할 때 쓰는 서식 지정자를 맞춰야만 합니다. 즉, %llu를 쓰면 됩니다.
오류 목록을 보면 경고가 모두 사라진 것을 볼 수 있습니다.
그러면 이걸 x86으로 바꾸고 빌드하면 어떻게 될까요?
sizeof는 해당 사양에 따라 다르다고 했습니다. 8비트 컴퓨터 시절부터 존재했던 언어라고요.
그러면 이제 이 오류 메시지는 해석이 가능할 겁니다.
32비트 환경에서 sizeof는 unsigned int를 반환합니다. 이는 비부호 32비트 정수입니다.
%llu를 쓰고 있으니 여기에는 long long을 써야 하는데, 어찌 너는 여기에 sizeof의 결과인 unsigned int를 전달했니? 크기가 안 맞잖아!
이런 의미입니다.
그렇다면 시스템에 따라 서식 지정자를 바꿔야 하는 불편함으로 언어를 설계했을까?
과거에는 그런 불편함이 있었으나 1999년에 ANSI C99 표준안에서 %zu 서식 지정자를 만들었습니다. u는 unsigned를 뜻하죠. 비부호이고, z는 sizeof에서 z를 가져다 쓴 것 같습니다.
오류가 발생하지 않습니다.
Visual Studio 버전에 따라 오류가 발생할 수 있습니다. C99 표준 지원 여부에 따라 오류가 표시될 수도 있습니다.
복잡한 얘기이죠.
sizeof 연산자로는 자료형의 크기를 구하기 때문에 보통 그 크기가 작습니다. int 크기로도 충분하죠. 그래서 보통은 %d나 %u 정도로 사용하고 소개하는 것 같습니다.
이 답변은 사이트 관리자가 작성했습니다(저자 아님).
아.. 이런 역사가 있었다니
자세한 설명 감사합니다.
그렇다면 그냥 sizeof 같은 경우는 말씀해주신대로
보통 그 크기가 크지 않으니
int로 충분하니까 %d를 쓰는구나
받아들이고 넘어가면 될까요?
네, 그렇게 이해하고 넘어가면 됩니다.
C99 표준을 지원한다면 %zu를 써도 됩니다(보통은 교수님이나 강사도 모름).
대학교에서는 Dev-C++을 쓰는 경우가 있는데 그러면 %zu가 동작하지 않습니다. 개발이 중단된 상태이고 윈도 8에서도 실행이 안 되어서 윈도 7 호환 모드로 실행해야 할 정도로 오래된 컴파일러라서 C99 표준도 지원하지 않습니다. 2020년 이후 오픈 소스 포크(fork, 복제본)로 다시 개발이 되는 모양이지만, 여전히 최신 버전과는 거리가 멉니다.
%d를 쓰고 경고는 무시하는 정도. 단, 올바른 코딩은 아니라는 것은 인지하고 있다. 정도이고, 면접 시에 위 내용을 설명할 수 있으면 매우 높은 점수를 받을 수 있다 정도입니다.