C언어의 자료형
자료형 | 크기 | 값의 표현범위 | |
정수형 | char | 1Byte | $-2^7 \leq \text{char} \leq 2^7-1$ |
short | 2Byte | $-2^{15} \leq \text{short} \leq 2^{15}-1$ | |
int | 4Byte | $-2^{31} \leq \text{int} \leq 2^{31}-1$ | |
long | 4Byte | $-2^{31} \leq \text{long} \leq 2^{31}-1$ | |
long long | 8Byte | $-2^{63} \leq \text{long long} \leq 2^{63}-1$ | |
실수형 | float | 4Byte | $-3.4*e^{38} \leq \text{long long} \leq 3.4*e^{38}$ 의 근사치 |
double | 8Byte | $-1.7*e^{308} \leq \text{long long} \leq 1.7*e^{308}$ 의 근사치 | |
long double | 8Byte 이상 | double 이상의 표현 범위 |
`sizeof` 함수를 사용해 변수, 상수 및 자료형의 크기를 확인할 수 있다.
#include <stdio.h>
int main(void)
{
char ch=9;
int inum=1052;
double dnum=3.1415;
printf("변수 ch의 크기 : %d \n", sizeof(ch)); # 1
printf("변수 inum의 크기 : %d \n", sizeof(inum)); # 4
printf("변수 dnum의 크기 : %d \n", sizeof(dnum)); # 8
printf("Dtype char의 크기 : %d \n", sizeof(char)); # 1
printf("Dtype int의 크기 : %d \n", sizeof(int)); # 4
printf("Dtype long의 크기 : %d \n", sizeof(long)); # 8
printf("Dtype long long의 크기 : %d \n", sizeof(long long)); # 8
printf("Dtype float의 크기 : %d \n", sizeof(float)); # 4
printf("Dtype double의 크기 : %d \n", sizeof(double)); # 8
return 0;
}
정수형 데이터의 표현 및 처리를 위한 자료형 선택
일반적인 경우에는 `int`형이 적합하다.
cpu가 연산하기에 가장 적합한 데이터의 크기가 int형이기 때문에, 정수형 변수에 대한 연산이 동반되면 int형으로 형 변환이 이루어진다.
char, short형 변수의 경우는 연산이 수반되지 않으면서(혹은, 최소한의 연산만 요구되면서) 많은 수의 작은 범위 내의 정수형 데이터를 표현한다면 char, short 형이 적합하다.
#include <stdio.h>
int main(void){
char num1=1, num2=2, result1=0;
short num3=300, num4=400, result2=0;
printf("size of num1 & num2 : %d, %d \n", sizeof(num1), sizeof(num2)); // 1, 1
printf("size of num3 & num4 : %d, %d \n", sizeof(num3), sizeof(num4)); // 2, 2
printf("size of char add : %d \n", sizeof(num1+num2)); // 4
printf("size of short add : %d \n", sizeof(num3+num4)); // 4
result1=num1+num2;
result2=num3+num4;
printf("size of result1 & result2 : %d, %d \n", sizeof(result1), sizeof(result2)); // 1, 2
return 0;
}
위의 예시에서 볼 수 있듯, `num1+num2`나 `num3+num4`의 경우에는 자동으로 연산 과정에서 `int`형 변환이 일어나 4byte의 크기를 가지며,
`result1`나 `result2`의 경우에는 이미 `char`, `short`의 자료형이 지정되어 있으므로 각각 1byte, 2byte의 크기를 갖는 것을 확인할 수 있다.
실수형 데이터의 표현 및 처리를 위한 자료형 선택
실수값의 표현범위 자체는 `float`, `double` 충분하지만, 소수부의 크기는 `float` < `double` 이기 때문에 `double`의 실수 표현 정밀성이 더 높다
실수의 printf, scanf
# include <stdio.h>
int main(void){
double rad;
double area;
printf("원의 반지름 입력 : ");
scanf("%lf", &rad); // scanf : %lf
// 원의 반지름 입력 : 2.4
area = rad*rad*3.1415;
printf("원의 넓이 : %f \n", area); // printf : %f
// 원의 넓이 : 18.095040
return 0;
}
unsigned : 0과 양의 정수만 표현
정수 자료형 앞에는 `unsigned`를 붙일 수 있다.
`unsigned`가 붙으면, MSB(부호를 나타내는 bit)도 데이터의 크기를 표현하는데 사용할 수 있으므로, 범위가 양수로 제한되며 크기가 두배로 늘어난다.
정수 자료형 | 크기 | 값의 표현범위 |
char | 1byte | $-2^7 \leq \text{char} \leq 2^7-1$ |
unsigned char | $0 \leq \text{unsigned char} \leq 2^7-1 + 2^7$ | |
short | 2byte | $-2^{15} \leq \text{short} \leq 2^{15}-1$ |
unsigned short | $0 \leq \text{unsigned short} \leq 2^{15}-1+2^{15}$ | |
int | 4byte | $-2^{31} \leq \text{int} \leq 2^{31}-1$ |
unsigned int | $0 \leq \text{unsigned int} \leq 2^{31}-1+2^{31}$ | |
long | $-2^{31} \leq \text{long} \leq 2^{31}-1$ | |
unsigned long | $0 \leq \text{unsigned long} \leq 2^{31}-1+2^{31}$ | |
long long | 8byte | $-2^{63} \leq \text{long long} \leq 2^{63}-1$ |
unsigned long long | $0 \leq \text{unsigned long} \leq 2^{63}-1+2^{63}$ |
ASCII 코드
컴퓨터는 문자를 직접적으로 표현, 저장하지 못한다. 따라서, 문자에 맞는 고유한 숫자를 지정한 후에 문자로 변환하는 과정을 거친다.
// ASCII
#include <stdio.h>
int main(void){
char ch1='A', ch2=65;
int ch3='Z', ch4=90;
printf("%c %d \n", ch1, ch1); // A 65
printf("%c %d \n", ch2, ch2); // A 65
printf("%c %d \n", ch3, ch3); // Z 90
printf("%c %d \n", ch4, ch4); // Z 90
return 0;
}
위의 예시를 통해 `A=65`, `Z=90`임을 알 수 있고, 다른 문자도 이와 같은 방식으로 고유한 숫자가 부여되어 있음을 알 수 있다.
*문자는 `char`자료형으로 충분히 표현가능하기 때문에 `char`로 저장하는 것이 일반적이고 효과적이지만, 다른 정수 자료형도 사용은 가능하다.
리터럴 상수
`int num=30+40;`의 경우 30, 40의 값에는 변수를 지정하지 않았지만, 그럼에도 불구하고 메모리 공간 어딘가에 저장되어야 연산이 가능하다.
이렇게 저장되는 값은 이름이 존재하지 않기 때문에 변경이 불가능한 '상수'이며, `리터럴 상수`라고 한다.
각 자료형에 따른 리터럴 상수는 일반적으로, 다음과 같은 자료형으로 저장된다.
- 정수 : `int`
- 실수 : `double`
- 문자 : `int`
접미사를 이용한 상수 표현
// literal const
#include <stdio.h>
int main1(void){
float num1=5.789; // num1(float) = 5.789(double) -> warning
float num2=3.24 + 5.12; // num2(float) = 3.24+5.12(double) -> warning
return 0;
}
int main2(void){
float num1=5.789f; // float=float -> no warning
float num2=3.24f + 5.12F; // float=float -> no warning
return 0;
}
위에서 언급했듯, 일반적으로 상수에 사용되는 자료형이 정해져 있기 때문에, `main1`함수의 경우에는 자료형의 불일치로 인한 warning이 발생하지만, `main2`처럼 상수 뒤에 접미사를 붙여 자료형을 통일시켜주면 그렇지 않다.
사용 가능한 접미사의 종류는 다음과 같다.
접미사 | 자료형 | 사용 예시 | |
정수형 | U | unsigned int | unsigned int = 1025U |
L | long | long n = 2467L | |
UL | unsigned long | unsigned long = 3456UL | |
LL | long long | long long n = 2768LL | |
ULL | unsigned long long | unsigned long long = 8979 ULL | |
실수형 | F | float | float f = 3.15f |
L | long double | long double f = 5.789L |
이름을 지니는 '심볼릭 상수'
#include <stdio.h>
int main(void){
const int MAX=10; // MAX라는 이름의 상수(값 변경 불가)
const double PI=3.1415; // PI라는 이름의 상수(값 변경 불가)
const int MAX; // MAX라는 이름의 상수를 쓰레기 값으로 초기화
MAX = 100; // 값 변경 불가하므로, ERROR!
return 0;
}
심볼릭 상수는 값을 변경할 수 없는 '상수'의 성질은 그대로 가지고 있지만 이름을 부여한 경우이다.
상수의 이름은 모두 대문자로 표기하는 것이 관례이다.
자동 형 변환
double num1=245; // 245 -> 245.0
int num2=3.1415; // 3.1415 -> 3
int num3=129;
char ch=num3; // int -> char : Byte수가 차이나므로, 메모리 값의 손실 발생
// 00000000 00000000 00000000 10000001 -> 10000001
형 변환을 거치게 되면, 몇가지 오차 혹은 값의 손실이 발생하게 된다.
- 정수 $\rightarrow$ 실수 : 오차가 발생($3 \rightarrow 3.0)
- 실수 $\rightarrow$ 정수 : 소수점 이하의 값이 소멸됨
- 큰 정수(실수) $\rightarrow$ 작은 정수(실수) : 작은 데이터 형에 맞춰 바이트가 소멸됨
int main(void)
{
double num1=245;
int num2=3.1415;
int num3=129;
char ch=num3;
printf("정수 245를 실수로: %f \n", num1); // 245.000000
printf("실수 3.1415를 정수로: %d \n", num2); // 3
printf("큰 정수 129를 작은 정수로: %d \n", ch); // -127
return 0;
}
정수의 승격에 의한 자동 형 변환
정수의 연산 과정에서 CPU가 처리하기에 가장 용이한 데이터 타입은 `int`이기 때문에, 그 과정에서 자동 형 변환이 발생하게 된다.
int main(void)
{
short num1=15, num2=25;
short num3=num1+num2; // 해당 과정에서 num1, num2가 int로 자동 형 변환됨
return 0;
}
자료형 불일치로 인해 발생하는 자동 형 변환
데이터를 연산하는 과정에서, 두 피연산자의 자료형이 불일치하는 경우에 자동 형 변환이 일어난다.
ex) `double num1=5.15 + 19` $\rightarrow$ 19가 19.0으로 자동 형 변환되어 연산이 진행된다.
이와 같은 산술 연산중에서의 자동 형 변환에는 나름의 규칙이 존재한다.
`int` $\rightarrow$ `long` $\rightarrow$ `long long` $\rightarrow$ `float` $\rightarrow$ `double` $\rightarrow$ `long double`
- 바이트 크기가 큰 자료형이 우선
- 정수형보다 실수형이 우선
이는 데이터의 손실을 최소화하기 위한 이유로 생각하면 이해하기 쉽다
강제 형 변환
#include <stdio.h>
int main(void)
{
int num1=3, num2=4;
double divResult;
divResult = num1/num2; // 정수/정수 이므로, 몫만을 계산하는 정수형 나눗셈 연산 결과가 저장됨
printf("divResult 나눗셈 결과 : %f \n", divResult); //divResult 나눗셈 결과 : 0.000000
double divResult2;
divResult2 = (double)num1/num2;
printf("divResult2 나눗셈 결과 : %f \n", divResult2); //divResult2 나눗셈 결과 : 0.750000
return 0;
}
위 예시에서 `divResult`는 정수/정수의 값을 저장하기 때문에 정수의 나눗셈의 결과로 그 몫의 값만을 저장하게 된다.
반면, `divResult2`는 나눗셈 연산 앞에 `(double)`을 붙여 형 변환을 명시해 주었기 때문에, 실수의 나눗셈 결과인 0.75의 값을 저장하게 된다.
따라서 위의 예시와 같이, 자동 형 변환이 발생하는 위치에 형 변환을 명시해주면 코드의 진행 과정을 이해하기가 더 수월할 수 있다.
'공부 > C언어' 카테고리의 다른 글
C언어 - (6) : 반복문 (0) | 2024.10.07 |
---|---|
C언어 - (5) : printf / scanf (3) | 2024.10.06 |
C언어 - (3) : 자료형 별 데이터의 표현 방식 / 비트 연산자 (2) | 2024.09.11 |
C언어 - (2) : 변수 / 연산자 (0) | 2024.09.09 |
C언어 - (1) (0) | 2024.09.05 |