정수 승격이 무엇인지 살펴보기 전에 아래 코드에 대한 결과부터 고민해보자
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]와 동일하다.
'Programming > C' 카테고리의 다른 글
구조체 비트 필드 structure bit field (0) | 2020.11.02 |
---|---|
C언어 포인터와 배열(C언어 pointer and array) (0) | 2020.10.19 |
C언어 포인터 활용(C언어 use of pointer) feat. swap함수 (0) | 2020.10.17 |
C언어 포인터 기본(C언어 basic pointer) (0) | 2020.10.17 |
가변길이 구조체(flexible array member) (0) | 2020.10.15 |