C언어 - 변수의 자료형
1. C언어 자료형
signedunsignedshortlongchar
intfloatdoubleautoregister
staticexternconstvolatile
1.1. 정수형 부호 지정자
Integer Sign Modifiers: 정수형 크기 부호 지정자는 일반적으로 char 또는 int와 같은 정수형 타입과 결합하여 사용한다.
- 단독으로 사용될 수도 있으나, 그 경우
int가 생략된 형태로 해석된다.
signed
unsigned
signed와 unsigned는 정수형 타입이 음수를 표현할지 여부를 결정하는 키워드이다.
signed는 부호 비트를 사용하여 음수와 양수를 모두 표현한다.unsigned는 음수를 표현하지 않고 0 이상의 값, 즉 양수만을 표현한다.
같은 1byte(8bit)라도 signed인지 unsigned인지에 따라 표현 가능한 값의 범위가 달라진다.
1.2. 정수형 크기 지정자
Integer Size Modifiers: 정수형 크기 지정자는 일반적으로 정수형 타입 int와 결합하여 사용한다.
- 단독으로 사용될 수도 있으나, 그 경우
int가 생략된 형태로 해석된다.
short
long
short
short는 정수형의 크기를 줄이기 위해 사용하는 지정자이다.
long
long은 정수형의 크기를 늘리기 위해 사용하는 지정자이다.int보다 더 큰 범위의 정수를 표현할 수 있지만, 32비트 시스템에서는int와long int의 크기가 같을 수 있다.
1.3. 기본 자료형
Fundamental Types: C 언어에서 정수, 실수 값을 표현하는 기본적인 내장 자료형이다.
1.3.1. 정수형
정수형은 소수점이 없는 정수 값을 표현한다. char는 문자 저장용으로 사용되지만, 정수형으로 분류되는 타입이다.
char
int
| 자료형 | 비트 수 | 16진수 범위 | 값의 범위 (10진수) |
|---|---|---|---|
| (signed) char | 8bit | 0x80 ~ 0x7f | -128 ~ 127 |
| unsigned char | 8bit | 0x00 ~ 0xff | 0 ~ 255 |
| short (int) | 16bit | 0x8000 ~ 0x7fff | -32,768 ~ 32,767 |
| unsigned short (int) | 16bit | 0x0000 ~ 0xffff | 0 ~ 65,535 |
| (signed) int | 32bit | 0x80000000 ~ 0x7fffffff | -2,147,483,648 ~ 2,147,483,647 |
| unsigned int | 32bit | 0x00000000 ~ 0xffffffff | 0 ~ 4,294,967,295 |
| long (int) | 32bit* | 0x80000000 ~ 0x7fffffff | -2,147,483,648 ~ 2,147,483,647 |
| unsigned long (int) | 32bit* | 0x00000000 ~ 0xffffffff | 0 ~ 4,294,967,295 |
- 일반적으로 최상위 비트(MSB, Most Significant Bit)를 부호 비트로 해석하며, 2의 보수 표현 방식을 사용해 하나의 덧셈기로 양수와 음수 연산을 처리함으로써 하드웨어를 단순화하고 연산을 일관되게 한다.
※ C90 기준에서 정수형 타입은 기본값이 signed이다. 따라서 signed를 명시하지 않아도 컴파일러는 이를 기본적으로 signed로 해석하며, 일반적으로 생략한다. short와 long도 int를 수식하는 지정자이므로, int는 기본값으로 간주되어 생략할 수 있다.
※ C99 이후에는 long long int가 도입되어 64비트 정수 표현이 가능하다.
| 자료형 | 비트 수 | 16진수 범위 | 값의 범위 (10진수) |
|---|---|---|---|
| (signed) long long int | 64bit | 0x8000000000000000 ~ 0x7fffffffffffffff |
-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 |
| unsigned long long int | 64bit | 0x0000000000000000 ~ 0xffffffffffffffff |
0 ~ 18,446,744,073,709,551,615 |
1.3.2. 실수형
실수형은 소수점을 포함한 실수 값을 표현하며, 부동소수점 방식으로 근사값을 저장한다.
float
double
| 자료형 | 저장 크기 | 값의 범위 (10진수) | 정밀도 (10진수) |
|---|---|---|---|
| float | 4 bytes | 1.2E-38 ~ 3.4E+38 | 6 decimal places |
| double | 8 bytes | 2.2E-308 ~ 1.8E+308 | 15 decimal places |
| long double | 10 bytes* | 3.4E-4932 ~ 1.1E+4932 | 19 decimal places |
float와double은 부호(Sign), 지수(Exponent), 가수(Mantissa)로 실수를 표현한다.- 지수는 표현 가능한 범위를, 가수는 정밀도를 결정하며,
float는 가볍고 빠른 대신 범위와 정밀도가 제한적이고double은 더 넓은 범위와 높은 정밀도를 제공한다.
※ long double의 저장 크기와 범위는 플랫폼과 컴파일러에 따라 달라질 수 있다.
부동소수점 방식
부동소수점 방식은 실수를 저장할 때 소수점 위치를 자유롭게 옮기며, 숫자를 정확히 그대로 저장하지 않고 가장 가까운 이진수 값으로 근사 저장하는 방식이다.
#include <math.h>
double a = 0.1;
double b = 0.2;
if ((a + b) != 0.3) {
/* condition is true due to floating-point precision error */
}
if (fabs((a + b) - 0.3) < 1e-9) {
/* treated as equal */
}
- 컴퓨터는
0.1과0.2를 정확한 값이 아니라 가장 가까운 이진수 값으로 저장한다. - 그래서
a + b의 실제 내부 값은0.30000000000000004처럼 실제값에 약간 어긋난 값이 된다. - 그 결과, 사람이 보기에는 같아 보이는
0.3과 같다고 판단하지 못한다.
1.4. 저장 클래스 지정자
Storage Class Specifier: 저장 클래스 지정자는 변수의 수명과 접근 범위를 결정하는 키워드이다.
auto
register
static
extern
1.4.1. auto
- 블록 내부에서 선언되는 지역 변수의 기본 저장 클래스이다.
- 별도로 지정하지 않아도 모든 지역 변수는 기본적으로
auto이며, 일반적으로 명시하지 않는다. - C99 이후
auto는 사실상 의미 없는 키워드이다.
auto int x; /* same as int x; */
1.4.2. register
- 변수를 CPU 레지스터에 저장하도록 요청하는 지정자이다.
- 접근 속도를 빠르게 하기 위한 힌트이며, 실제로 레지스터에 저장될지는 컴파일러가 결정한다.
- 주소 연산자(&)를 사용할 수 없다.
register int i;
※ C11 이후 대부분의 컴파일러에서는 최적화를 자동으로 수행하므로 register 키워드는 사실상 의미가 없다.
1.4.3. static
1.4.3.1. 지역 변수
- 수명은 프로그램 전체, 스코프는 블록 내부이다.
- 함수 호출이 끝나도 값이 유지된다.
void func() {
static int count = 0;
count++;
}
1.4.3.2. 전역 변수
- 파일 내부에서만 접근 가능하게 만든다.
- 다른 소스 파일에서 참조할 수 없다.
static int global;
1.4.4. extern
- 다른 파일에 정의된 전역 변수를 참조할 때 사용한다.
- 실제 저장 공간을 할당하지 않고 선언만 제공한다.
extern int shared;
1.5. 타입 한정자
Type Qualifier: 타입 한정자는 변수의 의미와 동작을 제한하는 키워드이며, const는 수정 불가, volatile은 외부 변경 가능성을 나타낸다.
const
volatile
1.5.1. const
const는 변수의 값을 변경할 수 없도록 제한하는 한정자이다.- 선언 이후에는 대입을 통해 값을 수정할 수 없다.
const int max = 10;
/* max = 20; // compilation error */
1.5.2. volatile
volatile은 변수의 값이 외부에 의해 변경될 있으므로, 항상 메모리에서 최신 값을 사용하게 하는 표시이다.- 컴파일러에게 최적화를 하지 말고, 항상 메모리에서 값을 다시 읽도록 지시한다.
volatile const int reg;
하드웨어 레지스터나 인터럽트에 의해 변경되는 변수에 사용된다.
※ volatile은 멀티스레드 동기화를 보장하지 않으며, atomic 연산이나 뮤텍스(mutex)를 대체하지 않는다.
2. C언어 자료형 출력
2.1. 예제
다음 예제는 여러 자료형의 크기를 출력한다.
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#define SIZEOF_BITS(type) (sizeof(type) * 8)
int main(void)
{
/* Character types */
printf("%-19s : %zu byte (%zu bit)\n", "char", sizeof(char), SIZEOF_BITS(char));
printf("%-19s : %zu byte (%zu bit)\n", "signed char", sizeof(signed char), SIZEOF_BITS(signed char));
printf("%-19s : %zu byte (%zu bit)\n", "unsigned char", sizeof(unsigned char), SIZEOF_BITS(unsigned char));
printf("\n");
/* Boolean type */
printf("%-19s : %zu byte (%zu bit)\n", "bool", sizeof(bool), SIZEOF_BITS(bool));
printf("\n");
/* Integer types */
printf("%-19s : %zu byte (%zu bit)\n", "short", sizeof(short), SIZEOF_BITS(short));
printf("%-19s : %zu byte (%zu bit)\n", "unsigned short", sizeof(unsigned short), SIZEOF_BITS(unsigned short));
printf("%-19s : %zu byte (%zu bit)\n", "int", sizeof(int), SIZEOF_BITS(int));
printf("%-19s : %zu byte (%zu bit)\n", "unsigned int", sizeof(unsigned int), SIZEOF_BITS(unsigned int));
printf("%-19s : %zu byte (%zu bit)\n", "long", sizeof(long), SIZEOF_BITS(long));
printf("%-19s : %zu byte (%zu bit)\n", "unsigned long", sizeof(unsigned long), SIZEOF_BITS(unsigned long));
printf("%-19s : %zu byte (%zu bit)\n", "long long", sizeof(long long), SIZEOF_BITS(long long));
printf("%-19s : %zu byte (%zu bit)\n", "unsigned long long", sizeof(unsigned long long), SIZEOF_BITS(unsigned long long));
printf("\n");
/* Floating-point types */
printf("%-19s : %zu byte (%zu bit)\n", "float", sizeof(float), SIZEOF_BITS(float));
printf("%-19s : %zu byte (%zu bit)\n", "double", sizeof(double), SIZEOF_BITS(double));
printf("%-19s : %zu byte (%zu bit)\n", "long double", sizeof(long double), SIZEOF_BITS(long double));
printf("\n");
/* Fixed-width integer types from stdint.h */
printf("%-19s : %zu byte (%zu bit)\n", "int8_t", sizeof(int8_t), SIZEOF_BITS(int8_t));
printf("%-19s : %zu byte (%zu bit)\n", "uint8_t", sizeof(uint8_t), SIZEOF_BITS(uint8_t));
printf("%-19s : %zu byte (%zu bit)\n", "int16_t", sizeof(int16_t), SIZEOF_BITS(int16_t));
printf("%-19s : %zu byte (%zu bit)\n", "uint16_t", sizeof(uint16_t), SIZEOF_BITS(uint16_t));
printf("%-19s : %zu byte (%zu bit)\n", "int32_t", sizeof(int32_t), SIZEOF_BITS(int32_t));
printf("%-19s : %zu byte (%zu bit)\n", "uint32_t", sizeof(uint32_t), SIZEOF_BITS(uint32_t));
printf("%-19s : %zu byte (%zu bit)\n", "int64_t", sizeof(int64_t), SIZEOF_BITS(int64_t));
printf("%-19s : %zu byte (%zu bit)\n", "uint64_t", sizeof(uint64_t), SIZEOF_BITS(uint64_t));
printf("\n");
return 0;
}
Ubuntu 24.04(x86_64)
char : 1 byte (8 bit)
signed char : 1 byte (8 bit)
unsigned char : 1 byte (8 bit)
bool : 1 byte (8 bit)
short : 2 byte (16 bit)
unsigned short : 2 byte (16 bit)
int : 4 byte (32 bit)
unsigned int : 4 byte (32 bit)
long : 8 byte (64 bit)
unsigned long : 8 byte (64 bit)
long long : 8 byte (64 bit)
unsigned long long : 8 byte (64 bit)
float : 4 byte (32 bit)
double : 8 byte (64 bit)
long double : 16 byte (128 bit)
int8_t : 1 byte (8 bit)
uint8_t : 1 byte (8 bit)
int16_t : 2 byte (16 bit)
uint16_t : 2 byte (16 bit)
int32_t : 4 byte (32 bit)
uint32_t : 4 byte (32 bit)
int64_t : 8 byte (64 bit)
uint64_t : 8 byte (64 bit)
※ 자료형의 크기는 컴파일러 구현과 OS 및 아키텍처 환경에 따라 서로 다를 수 있다. → Fixed-width Integer Types 사용
2.2. Void Type
void는 "값이 없다"는 의미의 특수 타입이다.
함수 매개변수가 없을 때, 함수 반환 값이 없을 때 사용한다.
stdbool.h
void init(void); /* no parameters and no return value */
※ 포인터에서 void는 자료형이 정해지지 않은 범용 포인터(void *)를 의미한다.
2.3. Boolean type
C99 기준
<stdbool.h>헤더에서bool을 정의true/false매크로도 함께 제공
stdbool.h
#define bool _Bool
#define true 1
#define false 0
- 크기는 1 byte, 내부적으로는 정수
_Bool는 실제로unsigned char정수 타입- 0이 아니면 참
- 정수 값이 0이 아니면
true, 0이면false로 해석된다. bool에 0이 아닌 정수를 할당할 수 있지만, 자동으로 0 또는 1로 정규화(normalize)된다.
2.4. Fixed-width Types
플랫폼과 컴파일러에 관계없이 정수의 크기를 동일하게 보장하기 위한 용도로 사용된다.
stdint.h
typedef __int8_t int8_t;
typedef __int16_t int16_t;
typedef __int32_t int32_t;
typedef __int64_t int64_t;
typedef __uint8_t uint8_t;
typedef __uint16_t uint16_t;
typedef __uint32_t uint32_t;
typedef __uint64_t uint64_t;
Ubuntu 24.04(x86_64)
typedef signed char __int8_t;
typedef short __int16_t;
typedef int __int32_t;
typedef long long __int64_t;
typedef unsigned char __uint8_t;
typedef unsigned short __uint16_t;
typedef unsigned int __uint32_t;
typedef unsigned long long __uint64_t;
- Fixed-width Integer Types는 정수형의 비트 수를 정확히 고정하기 위해 사용된다.
- 플랫폼이나 컴파일러가 달라도 항상 동일한 크기와 범위를 보장한다.
- 이로 인해 이식성과 바이너리 호환성이 중요한 코드에서 활용된다.