Skip to content

2. 정수 자료형


1. 키워드

  • 정수 자료형
  • 오버플로우(Overflow)와 언더플로우(Underflow)


2. 정수 자료형 사용하기

  • C에서는 다양한 형태의 자료형을 제공한다.
  • 이번에는 정수 자료형과 부호에 대해 알아보자.
  • 정수형 자료형은 크게 char, int 타입이 있으며 앞에 부호 키워드(signed, unsigned)와 크기(short, long)을 붙여서 특성을 정의할 수 있다.


1] signed

  • 부호 있는 정수를 표현한다.
  • 보통 signed 키워드는 생략한다.

2] unsigned

  • 부호 없는 정수를 표현한다.
  • 따라서 값은 0부터 시작하게 된다.


  • 다음은 정수 자료형 키워드를 조합했을 때 자료형의 크기와 저장할 수 있는 값의 범위이다.


001


  • 주요 정수 자료형의 크기와 범위를 그림으로 표현하면 다음과 같은 모양이 된다.
  • 여기서 int, unsigned int 타입과 같이 자료형이 같다면 부호 여부에 관계없이 크기는 같다.
  • 즉, 부호 있는 정수는 음수를 포함하므로 그만큼 양수의 범위가 줄어든다.


002


long 타입

  • long 타입은 운영체제와 플랫폼마다 크기가 다르다.


003


3. 정수형 변수 선언하기

  • 다음 내용을 입력한 뒤 실행해 보자.


#include <stdio.h>

int main()
{
    char num1 = -10;                       // 1바이트 부호 있는 정수형으로 변수를 선언하고 값 할당
    short num2 = 30000;                    // 2바이트 부호 있는 정수형으로 변수를 선언하고 값 할당
    int num3 = -1234567890;                // 4바이트 부호 있는 정수형으로 변수를 선언하고 값 할당
    long num4 = 1234567890;                // 4바이트 부호 있는 정수형으로 변수를 선언하고 값 할당
    long long num5 = -1234567890123456789; // 8바이트 부호 있는 정수형으로 변수를 선언하고 값 할당

    // char, short, int 타입은 %d로 출력하고 long 타입은 %ld, long long 타입은 %lld로 출력
    printf("%d %d %d %ld %lld\n", num1, num2, num3, num4, num5);

    return 0;
}

// -10 30000 -1234567890 1234567890 -1234567890123456789


  • 부호 있는 정수 자료형으로 변수를 만든 뒤 값을 할당해 봤다.
  • 보통 부호 있는 정수는 signed를 생략한다.
  • 또한, shortlong 타입도 int 타입을 생략하여 사용한다.
  • 여기서 char 타입은 문자(Character)를 뜻하지만 기본적으로 정수형이다.
  • printf 함수에서 char, short, int 타입은 서식 지정자 %d로 출력하지만, long 타입은 %ld를 사용하고 long long 타입은 %lld를 사용한다.


  • 이번에는 부호 없는 정수 자료형을 사용해 보자.


#include <stdio.h>

int main()
{
    unsigned char num1 = 200;                       // 1바이트 부호 없는 정수형으로 변수를 선언하고 값 할당
    unsigned short num2 = 60000;                    // 2바이트 부호 없는 정수형으로 변수를 선언하고 값 할당
    unsigned int num3 = 41234567890;                // 4바이트 부호 없는 정수형으로 변수를 선언하고 값 할당
    unsigned long num4 = 41234567890;               // 4바이트 부호 없는 정수형으로 변수를 선언하고 값 할당
    unsigned long long num5 = 12345678901234567890; // 8바이트 부호 없는 정수형으로 변수를 선언하고 값 할당

    // unsigned char, unsigned short, unsigned int 타입은 %u로 출력하고
    // unsigned long 타입은 %lu, unsigned long long 타입은 %llu로 출력
    printf("%u %u %u %lu %llu\n", num1, num2, num3, num4, num5); // 200 60000 4123456789 4123456789 12345678901234567890

    return 0;
}


  • 부호 없는 정수 자료형은 앞에 unsigned 키워드를 붙여주면 된다.
  • printf 함수에서 unsigned char, unsigned short 타입은 서식 지정자 %d로도 충분히 출력할 수 있지만 unsigned int 타입은 %u, unsigned long 타입은 %lu, unsigned long long 타입은 %llu로 출력해야 한다.
  • 참고로 GCC에서 컴파일 경고를 방지하려면 4123456789, 12345678901234567890 뒤에 자료형 크기에 맞는 정수 리터럴 접미사를 붙이면 된다.


4. 오버플로우와 언더플로우 알아보기

  • 다음 내용을 입력한 뒤 실행해 보자.


#include <stdio.h>

int main()
{
    char num1 = 128;          // char 타입에 저장할 수 있는 최댓값 127보다 큰 수를 할당 -> 오버플로우 발생
    unsigned char num2 = 256; // unsigned char 타입에 저장할 수 있는 최댓값 255보다 큰 수를 할당 -> 오버플로우 발생

    // 저장할 수 있는 범위를 넘어서므로 최솟값부터 다시 시작
    printf("%d %u\n", num1, num2); // -128 0

    return 0;
}


  • num1128을 넣었는데 실제 출력에서는 -128이 나와버렸다.
  • char 타입에 저장할 수 있는 최댓값은 127이라서 오버플로우가 발생했기 때문이다.
  • 즉, 128127보다 1이 많아서 저장할 수 있는 범위를 넘어섰고, 다시 최솟값부터 시작하게 되므로 -128이 나온다.
  • 마찬가지로 num2unsigned char 타입에 저장할 수 있는 최댓값은 255인데 256을 넣으면 범위를 넘어서므로 최솟값인 0부터 다시 시작하게 된다.
  • char 타입 이외에도 short, int, long, long long 타입 등도 저장할 수 있는 범위를 넘어서면 최솟값부터 다시 시작하게 된다.
  • 반대로 최솟값보다 작아지면 언더플로우가 발생하여 최댓값부터 다시 시작하게 된다.


004


5. 자료형 크기 구하기

  • 이번에는 자료형(타입)의 크기를 바이트 단위로 구하는 sizeof 연산자에 대해 알아보자.
  • 먼저 sizeof 연산자는 다음과 같은 형식으로 사용한다.


sizeof 표현식
sizeof(자료형)
sizeof(표현식)


  • 다음 내용을 입력한 뒤 실행해 보자.


#include <stdio.h>

int main()
{
    int num1 = 0;
    int size;

    size = sizeof num1; // 변수 num1의 자료형 크기를 구함

    printf("num1의 크기: %d\n", size); // num1의 크기: 4

    return 0;
}


  • sizeof 표현식 형식으로 자료형의 크기를 구할 수 있다.
  • 표현식은 변수, 상수, 배열 등 프로그래머가 만들어낸 요소를 뜻한다.
  • 여기서 정수형 변수 num1의 자료형 크기를 구했고, 4가 출력되었다.
  • 단, sizeof int와 같은 형식으로는 자료형의 크기를 구할 수 없다.


  • 자료형의 크기를 직접 구하려면 다음과 같이 sizeof(자료형) 형식으로 사용한다.


int size;

size = sizeof(int);


  • sizeof(표현식) 형식도 사용할 수 있다.


  • 이제 각 정수 자료형의 크기를 구해 보자.


#include <stdio.h>

int main()
{
    printf("char: %d, short: %d, int: %d, long: %d, long long: %d\n",
           sizeof(char),       // sizeof로 char 타입의 크기를 구함
           sizeof(short),      // sizeof로 short 타입의 크기를 구함
           sizeof(int),        // sizeof로 int 타입의 크기를 구함
           sizeof(long),       // sizeof로 long 타입의 크기를 구함
           sizeof(long long)); // sizeof로 long long 타입의 크기를 구함

    return 0;
}

// char: 1, short: 2, int: 4, long: 8, long long: 8


  • 출력 결과에 따르면 char 타입은 1바이트, short 타입은 2바이트, int 타입은 4바이트, longlong long 타입은 8바이트이다.
  • 여기서 unsigned가 붙어도 크기는 같다.


6. 최솟값과 최댓값 표현하기

  • 이번에는 정수의 최솟값과 최댓값을 표현하는 방법을 알아보자.


  • 부호 있는 int 타입의 최솟값은 -2,147,483,648이라고 했지만, 이 값을 직접 넣어보면 컴파일 에러가 발생한다.
  • 따라서 정수의 최솟값을 표현하려면 limits.h 헤더 파일을 사용해야 한다.
  • 다음 내용을 입력한 뒤 실행해 보자.


#include <stdio.h>
#include <limits.h> // 자료형의 최댓값과 최솟값이 정의된 헤더 파일

int main()
{
    char num1 = CHAR_MIN;       // char 타입의 최솟값
    short num2 = SHRT_MIN;      // short 타입의 최솟값
    int num3 = INT_MIN;         // int 타입의 최솟값
    long num4 = LONG_MIN;       // long 타입의 최솟값
    long long num5 = LLONG_MIN; // long long 타입의 최솟값

    // char, short, int 타입은 %d로 출력하고
    // long 타입은 %ld로 출력, long long 타입은 %lld로 출력
    printf("%d %d %d %ld %lld\n", num1, num2, num3, num4, num5); // -128 -32768 -2147483648 -9223372036854775808 -9223372036854775808

    return 0;
}


  • CHAR_MIN, SHRT_MIN, INT_MIN, LONG_MIN, LLONG_MIN은 부호 있는 정수의 최솟값이다.
  • limits.h 헤더 파일에는 다음과 같이 부호 있는 정수와 부호 없는 정수의 최솟값과 최댓값이 정의되어 있다.


005


  • 다음과 같이 limits.h에 정의된 최댓값을 넘어서도 오버플로우가 발생한다.


#include <stdio.h>
#include <limits.h> // 자료형의 최댓값과 최솟값이 정의된 헤더 파일

int main()
{
    char num1 = CHAR_MAX + 1;       // char 타입의 최댓값보다 큰 수를 할당 -> 오버플로우 발생
    short num2 = SHRT_MAX + 1;      // short 타입의 최댓값보다 큰 수를 할당 -> 오버플로우 발생
    int num3 = INT_MAX + 1;         // int 타입의 최댓값보다 큰 수를 할당 -> 오버플로우 발생
    long long num4 = LLONG_MAX + 1; // long long 타입의 최댓값보다 큰 수를 할당 -> 오버플로우 발생

    // char, short, int 타입은 %d로 출력하고 long long 타입은 %lld로 출력
    // 부호 있는 정수에서 저장할 수 있는 범위를 넘어서면 최솟값부터 다시 시작
    printf("%d %d %d %lld\n", num1, num2, num3, num4); // -128 -32768 -2147483648 -9223372036854775808

    unsigned char num5 = UCHAR_MAX + 1;       // unsigned char 타입의 최댓값보다 큰 수를 할당 -> 오버플로우 발생
    unsigned short num6 = USHRT_MAX + 1;      // unsigned short 타입의 최댓값보다 큰 수를 할당 -> 오버플로우 발생
    unsigned int num7 = UINT_MAX + 1;         // unsigned int 타입의 최댓값보다 큰 수를 할당 -> 오버플로우 발생
    unsigned long long num8 = ULLONG_MAX + 1; // unsigned long long 타입의 최댓값보다 큰 수를 할당 -> 오버플로우 발생

    // unsigned char, unsigned short, unsigned int 타입은 %u로 출력하고
    // unsigned long long 타입은 %llu로 출력
    // 부호 없는 정수에서 저장할 수 있는 범위를 넘어서면 최솟값 0부터 다시 시작
    printf("%u %u %u %llu\n", num5, num6, num7, num8); // 0 0 0 0

    return 0;
}


  • 부호 있는 정수는 저장할 수 있는 범위를 넘어서면 최솟값(음수)부터 다시 시작하고, 부호 없는 정수는 범위를 넘어서면 최솟값인 0부터 다시 시작한다.


  • 마찬가지로 최솟값보다 작아지면 언더플로우가 발생한다.


#include <stdio.h>
#include <limits.h> // 자료형의 최댓값과 최솟값이 정의된 헤더 파일

int main()
{
    char num1 = CHAR_MIN - 1;       // char 타입의 최솟값보다 작은 수를 할당 -> 언더플로우 발생
    short num2 = SHRT_MIN - 1;      // short 타입의 최솟값보다 작은 수를 할당 -> 언더플로우 발생
    int num3 = INT_MIN - 1;         // int 타입의 최솟값보다 작은 수를 할당 -> 언더플로우 발생
    long long num4 = LLONG_MIN - 1; // long long 타입의 최솟값보다 작은 수를 할당 -> 언더플로우 발생

    // char, short, int 타입은 %d로 출력하고 long long 타입은 %lld로 출력
    // 부호 있는 정수에서 최솟값보다 작아지면 최댓값부터 다시 시작
    printf("%d %d %d %lld\n", num1, num2, num3, num4); // 127 32767 2147483647 9223372036854775807

    unsigned char num5 = 0 - 1;      // unsigned char 타입의 최솟값보다 작은 수를 할당 -> 언더플로우 발생
    unsigned short num6 = 0 - 1;     // unsigned short 타입의 최솟값보다 작은 수를 할당 -> 언더플로우 발생
    unsigned int num7 = 0 - 1;       // unsigned int 타입의 최솟값보다 작은 수를 할당 -> 언더플로우 발생
    unsigned long long num8 = 0 - 1; // unsigned long long 타입의 최솟값보다 작은 수를 할당 -> 언더플로우 발생

    // unsigned char, unsigned short, unsigned int 타입은 %u로 출력하고
    // unsigned long long 타입은 %llu로 출력
    // 부호 없는 정수에서 최솟값보다 작아지면 최댓값부터 다시 시작
    printf("%u %u %u %llu\n", num5, num6, num7, num8); // 255 65535 4294967295 18446744073709551615

    return 0;
}


  • 최솟값에서 1을 빼서 값이 더 작아지면 언더플로우가 발생하여 다시 한 바퀴 돌게 되므로 최댓값이 출력된다.


7. 크기가 표시된 정수 자료형 사용하기


  • 다음은 stdint.h 헤더 파일을 사용하여 크기가 표시된 정수 자료형으로 변수를 선언하는 방법이다.


#include <stdio.h>
#include <stdint.h> // 크기별로 정수 자료형이 정의된 헤더 파일

int main()
{
    int8_t num1 = -128;                 // 8비트(1바이트) 크기의 부호 있는 정수형 변수 선언
    int16_t num2 = 32767;               // 16비트(2바이트) 크기의 부호 있는 정수형 변수 선언
    int32_t num3 = 2147483647;          // 32비트(4바이트) 크기의 부호 있는 정수형 변수 선언
    int64_t num4 = 9223372036854775807; // 64비트(8바이트) 크기의 부호 있는 정수형 변수 선언

    // int8_t, int16_t, int32_t 타입은 %d로 출력하고 int64_t는 %lld로 출력
    printf("%d %d %d %lld\n", num1, num2, num3, num4); // -128 32767 2147483647 9223372036854775807

    uint8_t num5 = 255;                   // 8비트(1바이트) 크기의 부호 없는 정수형 변수 선언
    uint16_t num6 = 65535;                // 16비트(2바이트) 크기의 부호 없는 정수형 변수 선언
    uint32_t num7 = 4294967295;           // 32비트(4바이트) 크기의 부호 없는 정수형 변수 선언
    uint64_t num8 = 18446744073709551615; // 64비트(8바이트) 크기의 부호 없는 정수형 변수 선언

    // uint8_t, uint16_t, uint32_t 타입은 %u로 출력하고 uint64_t는 %llu로 출력
    printf("%u %u %u %llu\n", num5, num6, num7, num8); // 255 65535 4294967295 18446744073709551615

    return 0;
}


  • int8_t, int16_t, int32_t, int64_t 타입처럼 자료형 이름에 비트 단위로 크기가 표시되어 있다.
  • 그리고 부호 없는 자료형은 앞에 u를 붙여서 uint8_t, uint16_t, uint32_t, uint64_t처럼 표시되어 있다.
  • 이런 자료형은 크기를 정확하게 표현해야 하는 파일 압축 및 암호화, 네트워크 프로그래밍을 할 때 특히 유용하며, 일반적인 프로그래밍을 할 때도 처음부터 stdint.h를 사용하는 것이 좋다.
  • stdint의 최소, 최댓값은 stdint.h 헤더 파일 안에 정의되어 있으므로 limits.h 헤더 파일을 사용하지 않아도 된다.
  • 자료형과 마찬가지로 최소, 최댓값도 비트 단위로 크기가 표시되어 있으므로 간편하게 사용할 수 있다.


1] 부호 있는 정수 최솟값

  • INT8_MIN, INT16_MIN, INT32_MIN, INT64_MIN

2] 부호 있는 정수 최댓값

  • INT8_MAX, INT16_MAX, INT32_MAX, INT64_MAX

3] 부호 없는 정수 최솟값

  • 0

4] 부호 없는 정수 최댓값

  • UINT8_MAX, UINT16_MAX, UINT32_MAX, UINT64_MAX

References