2. 정수 자료형
1. 키워드
- 정수 자료형
- 오버플로우(Overflow)와 언더플로우(Underflow)
2. 정수 자료형 사용하기
- C에서는 다양한 형태의 자료형을 제공한다.
- 이번에는 정수 자료형과 부호에 대해 알아보자.
- 정수형 자료형은 크게
char
,int
타입이 있으며 앞에 부호 키워드(signed
,unsigned
)와 크기(short
,long
)을 붙여서 특성을 정의할 수 있다.
1] signed
- 부호 있는 정수를 표현한다.
- 보통
signed
키워드는 생략한다.
2] unsigned
- 부호 없는 정수를 표현한다.
- 따라서 값은
0
부터 시작하게 된다.
- 다음은 정수 자료형 키워드를 조합했을 때 자료형의 크기와 저장할 수 있는 값의 범위이다.
- 주요 정수 자료형의 크기와 범위를 그림으로 표현하면 다음과 같은 모양이 된다.
- 여기서
int
,unsigned int
타입과 같이 자료형이 같다면 부호 여부에 관계없이 크기는 같다. - 즉, 부호 있는 정수는 음수를 포함하므로 그만큼 양수의 범위가 줄어든다.
long
타입
long
타입은 운영체제와 플랫폼마다 크기가 다르다.
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
를 생략한다. - 또한,
short
와long
타입도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;
}
num1
에128
을 넣었는데 실제 출력에서는-128
이 나와버렸다.char
타입에 저장할 수 있는 최댓값은127
이라서 오버플로우가 발생했기 때문이다.- 즉,
128
이127
보다1
이 많아서 저장할 수 있는 범위를 넘어섰고, 다시 최솟값부터 시작하게 되므로-128
이 나온다. - 마찬가지로
num2
도unsigned char
타입에 저장할 수 있는 최댓값은255
인데256
을 넣으면 범위를 넘어서므로 최솟값인0
부터 다시 시작하게 된다. char
타입 이외에도short
,int
,long
,long long
타입 등도 저장할 수 있는 범위를 넘어서면 최솟값부터 다시 시작하게 된다.- 반대로 최솟값보다 작아지면 언더플로우가 발생하여 최댓값부터 다시 시작하게 된다.
5. 자료형 크기 구하기
- 이번에는 자료형(타입)의 크기를 바이트 단위로 구하는
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(자료형)
형식으로 사용한다.
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바이트,long
과long 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
헤더 파일에는 다음과 같이 부호 있는 정수와 부호 없는 정수의 최솟값과 최댓값이 정의되어 있다.
- 다음과 같이
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