본문 바로가기

공부/C언어

C언어 - (4) : 자료형/형 변환

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