본문 바로가기

Programming/C

정수 승격(integer promotion), 묵시적 형변환

반응형

정수 승격이 무엇인지 살펴보기 전에 아래 코드에 대한 결과부터 고민해보자

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
  
int main()
{
    char a, b;
 
    printf("sizeof(a) : %d, sizeof(b) : %d\n"sizeof(a), sizeof(b));
    printf("sizeof(a + b) : %d\n"sizeof(a + b));
 
    return 0;
}
                                
cs


위 코드를 실행하면, sizeof(a + b)는 몇이 출력이 될까?

char + char type이므로 당연히 char 타입의 size인 1(byte)이 출력 될 것이라 생각했다면

정수 승격에 대해 아직 모르고 있다는 것!


위 코드를 실행한 결과는 아래와 같다.


왜 sizeof(a + b)가 4 일까?


바로 정수 승격이 일어나기 때문이다.

정수들끼리의 연산을 할 때에는 char, short type들을 내부적으로 int로 바꾼 다음에 계산을 한다.

type이 다른 정수형들끼리 연산을 할 경우 구현 방법이 경우에 따라 달라지기 때문에 

int형으로만 연산을 지원하여 구현을 단순화 하는 방안을 택했다.



아래 코드는 형변환의 다른 예시이다.

아래 코드는 회사에서 프로젝트 관련 test코드를 작성하다 막상 test코드에 문제가 있어 시간을 날린 케이스이다.


~ 연산은 데이터를 invert 시키는 연산자이다.


1. original 데이터 배열이 있고, 

2. original 데이터 배열을 '~' 연산자를 통해 invert한 inverted 데이터 배열이 있다.

3. inverted 데이터 배열을 다시 '~' 연산자를 통해 invert하면 기존의 original 데이터 배열과 동일 할 것이다.


...라고 생각을 했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
 
#define SIZE    20
 
int main()
{
    unsigned char original[SIZE];
    unsigned char inverted[SIZE];
    int i;
    int result;
 
    for (i = 0; i < SIZE; i++)
        original[i] = i;
 
    for (i = 0; i < SIZE; i++)
        inverted[i] = ~original[i];
 
    for (i = 0, result = 0; i < SIZE; i++)
        if (original[i] != ~inverted[i])  {
            result = 1;
            break;
        }
    
    if (result == 1)
        printf("original[i] and ~inverted[i] has different value\n");
    else
        printf("original[i] and ~inverted[i] has same value\n");
    
    return 0;
}
 
cs


실행 결과 :



왜 다를까?


바로 정수 승격이 일어나기 때문이다.

기존에 사칙연산의 경우에만 정수 승격이 일어나는 줄 알았으나,


Integer types smaller than int are promoted when an operation is performed on them.
If all values of the original type can be represented as an int,
the value of the smaller type is converted to an int;
otherwise, it is converted to an unsigned int.
Integer promotions are applied as part of the usual arithmetic conversions
to certain argument expressions; operands of the unary +-, and ~ operators;
and operands of the shift operators.

출처 : https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules


즉, operation 작업을 수행 할 때, int type보다 작은 정수는 int 로 승격이 된다.

int 타입이 표현할 수 있는 범위의 값이라면 int로, 그렇지 않은 경우 unsigned int로 승격된다.

사칙연산의 경우 int로 들어가고, 단일연산자로서의 +와-, 그리고 ~, <<, >> 연산자를 사용할 때에도

정수 승격이 일어난다. 



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int main()
{
        unsigned char original[SIZE];
        unsigned char inverted[SIZE];
        int i;
        int result;;
 
        for (int i = 0; i < SIZE; i++)
                original[i] = i;
 
        for (int i = 0; i < SIZE; i++)
                inverted[i] = ~original[i];
 
        for (i = 0, result = 0; i < SIZE; i++)
                if (original[i] != ~inverted[i])  {
                        result = 1;
                        break;
                }
 
        if (result == 1)
                printf("original[i] and ~inverted[i] has different value\n");
        else
                printf("original[i] and ~inverted[i] has same value\n");
 
        printf("original[i]:%x \tinverted[i]:%x\n", original[i], inverted[i]);
        printf("original[i]:%x \t~inverted[i]:%x", original[i], ~inverted[i]);
 
        return 0;
}
 
cs

25,26 번째 print문을 추가해서 왜 값이 다른지 살펴보자.


위 코드에 대한 실행 결과는 아래와 같다

 

위와 같은 결과가 나온 이유는 다음과 같다.

15번째 라인에서, original[i]는 여전히 unsigned char일 것이다.

하지만 ~inverted[i] 는 ~ 연산을 하므로 int로 정수 승격이 일어났을 것이고,

unsigned char 변수(original[i])와 int변수(승격된 ~inverted[i])를 비교할(!=연산) 때 

또한번 unsigned char가 int로 승격 할 것이다.

그런 다음 "int 변수 != int 변수" 를  실행 할 것이다.



그럼 의도한 대로 하기 위해서는 ?

바로 19번째 줄처럼 int로 형변환 된 것을 다시 unsigned char로 명시적으로 타입캐스팅을 해주는 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
  
#define SIZE    20
 
int main()
{
        unsigned char original[SIZE];
        unsigned char inverted[SIZE];
        int i;
        int result;;
 
        for (int i = 0; i < SIZE; i++)
                original[i] = i;
 
        for (int i = 0; i < SIZE; i++)
                inverted[i] = ~original[i];
 
        for (i = 0, result = 0; i < SIZE; i++)
                if (original[i] != (unsigned char)~inverted[i])  {
                        result = 1;
                        break;
                }
 
        if (result == 1)
                printf("original[i] and ~inverted[i] has different value\n");
        else
                printf("original[i] and ~inverted[i] has same value\n");
 
        return 0;
}
 
cs

결과가 의도한 대로 나옴을 확인 할 수 있다.


다시 해석해보면


if (original[i] != (unsigned char)~inverted[i]) 

original[i]는 아무런 연산을 진행하지 않았으니 여전히 unsigned char 일 것이고,

inverted[i]는 "~"연산을 통해 int로 형변환이 되었을 것이다. (위 결과에서 ~inverted[i] 출력 값 ffffff00)

~inverted[i]를 다시 (unsigned char)로 타입캐스팅 하면 ~inverted[i]는 00이 될 것이다.

이게 중요함! 앞에 있던 ffffff 이 사라짐!


unsigned char 타입 != unsigned char 타입 이 될 것이다.

이 때 비교연산인 != 을 수행할 때 다시 int 타입으로 형변환이 일어나

int 타입 != int 타입이 될 것이다. 

~inverted[i]의 경우 ~연산을 할 때 생겼던 ffffff들을 unsigned char로 타입캐스팅을 해서 날려버렸으므로

현재 그냥 0이며, 이를 정수승격을 시켜도 여전히 0.으로 original[i]와 동일하다. 


반응형