Linux Programmer

C언어 : int의 크기 (몇몇 C언어 책이나 글의 오류를 잡자) 본문

컴퓨터 관련/C언어

C언어 : int의 크기 (몇몇 C언어 책이나 글의 오류를 잡자)

sunyzero 2020. 1. 17. 23:06

예전에 C언어를 잘못 배우는 사람들이 많아서 쓰게 된 "C언어 공부법과 추천"글의 후속으로, 구체적으로 어떤 내용들이 잘못 알려졌는지를 다뤄 볼 것이다. 이 내용들은 좋지 않은 C언어 서적들이나 인터넷에서 돌아다니는 강좌들에 반복적으로 나타나는 오류이다. 사실 알고보면 인터넷에 잘못된 오류를 쓴 사람들은 원래 잘못된 책으로 공부한 뒤에, 그 지식들을 정리한 뒤에 글을 쓰기 때문에 책으로부터 퍼지는 것과 다를바가 없다. 최근에는 많이 줄었지만 fflush(stdin) 같은 UB(undefined behavior)는 너무 흔하게 나타나서 C언어를 배운 사람중에 거의 대다수가 fflush(stdin)을 쓸 정도였다. 심지어 많은 C언어 책에도 fflush(stdin)을 마구잡이로 썼었다. ㅠㅠ

 

그래서 본인은 C언어 표준을 위반하는 내용들 중에 폐해가 큰 것들을 위주로 적도록 할 것이다. 가끔 폐해라고 볼 수는 없지만 이론쪽에서 틀린 내용도 가끔 설명할 때도 있을 것이다.(그러다보면 시스템쪽의 내용도 좀 다루게 될 것 같다.) 거창한 계획을 두고 정리하면서 쓰는게 아니라 생각나는대로 쓰는 것이라 딱히 특별한 순서대로 다루지는 않을 것이다. 물론 나중에 모아서 다시 정리할 수도 있겠지만....

 

* 변수의 크기

시험 출제 때문에 많은 C언어 책을 검수하다보니 변수의 크기가 잘못 설명된 책이 꽤 많았다. 예를 들어 "char는 1바이트, short은 2바이트, int는 4바이트입니다." 이런 책은 일단 틀린 책이라고 보면 된다.

 

C언어 표준을 보면 자료형의 크기는 고정적으로 정해져있는 것이 아니라 구현체에 따라서 다를 수 있음을 적어놓고 있다. 따라서 위와 같이 char 1바이트, short 2바이트, int 4바이트라고 말하는 사람들은 잘못 배운 것이다.

 

먼저 C언어 표준에서는 제공하는 5개의 signed integer 타입을 정리해보자.[1]

  • signed char
  • short int
  • int
  • long int
  • long long int

위 integer 타입들의 크기는 어떻게 될까? 우선 char는 기본 character set을 저장하기에 충분한 크기를 가지도록 설계된다. 현대적인 대부분의 시스템은 ASCII기반이므로 최소 8bit이상의 크기를 가질 가능성이 매우 높다. 하지만 이도 정확하게 고정된 것은 아니다. int는 실행 환경(execution environment)에서 자연스러운 크기(natural size)를 가지게 된다. 따라서 int는 대부분의 시스템에서 실행시 가장 합리적이고 빠른 동작을 보장하는 정수 크기를 가지는 경우가 많다.[1]

 

그러면 실제로 내가 사용하는 시스템의 int의 크기와 범위는 어떻게 되는지 살펴보자. 이를 확인하기 위해 간단한 예제를 하나 작성해보자. 이 예제는 C99 표준에 근거해서 작성한 예제이며, 현재 시스템의 int의 크기(단위:byte)와 범위 그리고 시스템에서 제공하는 가장 큰 signed integer의 크기(단위:byte)와 범위를 출력하도록 한 코드이다.

#include <stdio.h>
#include <stdint.h>    // intmax_t
#include <limits.h>    // INT_MAX/MIN
#include <inttypes.h>  // PRIdMAX
int main()
{
    printf("sizeof(int)      = %zd\n", sizeof(int));
    printf("INT_MIN ~ INT_MAX = %d ~ %d\n", INT_MIN, INT_MAX);
    printf("sizeof(intmax_t) = %zd\n", sizeof(intmax_t));
    printf("INTMAX_MIN ~ INTMAX_MAX = %"PRIdMAX" ~ %"PRIdMAX"\n", INTMAX_MIN, INTMAX_MAX);
    return 0;
}

위 코드의 파일명을 intmax.c라고 하자. 위 코드를 ARM7 Linux에서 빌드하여 실행하면 아래와 같은 결과가 출력된다.

 

참고로 빌드는 make를 이용해서 C99 표준에 맞춰 빌드하게 할 것이므로 make의 CFLAGS변수를 오버라이드 하는 명령어를 사용했다. (간혹 몇몇 책에 나오는 gcc -o ... 요렇게 명령하는 것은 진짜 처음 배울 때만 쓰는 명령이다. 일단 C나 C++언어를 배우겠다면 무조건 make를 배워야 한다. make 없이 직접 gcc, clang을 타이핑 쳐서 빌드하는 것은 Hello world 예제 외엔 쓰지 않는다고 보면 된다. make 기본 사용법만 배운다면 1~2시간이면 다 배운다.)

 

make  CFLAGS="-Wall -std=c99"  intmax
cc -Wall -std=c99    intmax.c   -o intmax

./intmax 
sizeof(int)      = 4
INT_MIN ~ INT_MAX = -2147483648 ~ 2147483647
sizeof(intmax_t) = 8
INTMAX_MIN ~ INTMAX_MAX = -9223372036854775808 ~ 9223372036854775807

 

C표준에서 int 타입들의 크기는 sizeof(int) <= sizeof(long int) <= sizeof(long long int) 의 관계를 가진다. 따라서 어떤 시스템은 int, long int가 같을 수도 있고 long int가 int보다 클 수도 있다. 이런 데이터 타입의 관계 때문에 long int가 64bit이고 pointer type이 64bit인 모델을 LP64라고 하고 long long int (64bit), ptr(64bit)인 시스템을 LLP64라고 부른다.

 

따라서 통신 시스템이나 데이터 관리하는 프로그램을 개발할 때 natural size를 가지는 int나 long int로 데이터를 주고받자고 하는 사람은 초짜인 것이다. 정확하게는 uint32_t 라고 하거나 uint64_t라고 하듯이 정확한 타입을 이야기 해야만 한다.(uint32_t는 unsigned int 32bit를 의미) 더군다나 int mode뿐만 아니라 XDR에 의해 padding까지 발생한다면 통신 프로토콜을 보고 프로그래밍을 할 때 실제 문서의 데이터와 전송 데이터의 크기가 달라질 수 있다.(예를 들어 int32_t가 섞인 데이터 구조인 1byte, 4byte, 4byte 를 전송한다고 하면 pack처리를 하지 않는다면 실제로는 9byte가 아니라 12byte가 전송될 수 있다.)

 

이게 뭔 대수인가라고 생각할 수도 있다. 하지만 데이터 타입의 크기는 예제를 짜는 수준에서는 큰 체감이 없지만 프로그램을 개발할 때는 심각한 문제를 일으키기도 한다. 예를 들어 구조체를 사용하다가 SIGBUS에러를 발생시킬 수 있는 문제가 대표적이다. 이 외에 printf에서도 크기를 잘못 알고 있으면 버그를 발생시켜 프로세스를 죽일 수도 있다. 이렇듯 데이터 타입의 크기는 생각보다 꽤 중요한 문제다.

 

일단 C언어 데이터 타입에 대한 글은 백진욱님이 자세하게 써둔 글이 있으니 한 번 읽어보기를 추천한다.

 

C 언어 데이터 모델의 간단한 정리 (LP32 ILP32 LP64 ILP64 LLP64) - 백진욱

 

 

C 언어 데이터 모델의 간단한 정리 (LP32 ILP32 LP64 ILP64 LLP64)

64비트 Objective-C에 대한 글을 쓰기 앞서서 C 언어 데이터 모델이 무엇인지 이야기하고자 한다. 64비트 시스템이 나오면서 기존의 플랫폼에 따른 타입들의 크기를 재정의 해야할 필요가 생겼는데 이에 대한 이야기이다. 플랫폼에 따라 변하는 기본 타입들은  int, void*(pointer), long, long long 등을 말한다. 물론 16…

www.jinukbaek.com

이제 누군가가 C언어에서 int는 몇 바이트입니까? 라고 물으면 "시스템에 따라서 가변적입니다. (어떤 시스템은 2바이트 혹은 4바이트, 8바이트 일 수도 있습니다)"라고 말해야 할 것이다.

 

그리고 어떤 통신이나 데이터 교환, 저장같은 실무를 한다면 가변적인 데이터 모델인 int, long이 아닌 int32_t, int64_t 같은 고정된 형식을 사용하는 것이 좋다.

 

... 다음에는 많은 사람들이 실수하는 C언어에는 없지만 있다고 착각하게 하는 call by reference나 sequence point, overflow와 underflow 같은 이야기를 써볼까 생각중이다. 물론 이것은 얼마든지 바뀔 수 있다.

 

* 참고

[1] Standard integer types - C1x n1570 ISO/IEC 9899 6.2.5 Types

2 Comments
댓글쓰기 폼