암묵적 유형 승격 규칙
이 게시물은 C의 암묵적 정수 프로모션, 특히 통상적인 산술 변환 및/또는 정수 프로모션에 의한 암묵적 프로모션에 관한 FAQ로 사용됩니다.
예1)
왜 255가 아닌 이상하고 큰 정수를 얻을 수 있을까요?
unsigned char x = 0;
unsigned char y = 1;
printf("%u\n", x - y);
예2)
"-1이 0보다 크다"는 것은 왜입니까?
unsigned int a = 1;
signed int b = -2;
if(a + b > 0)
puts("-1 is larger than 0");
예 3)
위의 예에서 유형을 변경하는 이유는 무엇입니까?short
문제를 해결하시겠습니까?
unsigned short a = 1;
signed short b = -2;
if(a + b > 0)
puts("-1 is larger than 0"); // will not print
(이러한 예는 16비트가 짧은 32비트 또는 64비트 컴퓨터를 대상으로 하고 있습니다).
C는 식에서 사용되는 오퍼랜드의 정수 유형을 암묵적으로 사일런트하게 변경하도록 설계되어 있습니다.언어에 의해 컴파일러가 피연산자를 더 큰 타입으로 변경하거나 피연산자의 부호성을 변경하도록 강제하는 경우가 몇 가지 있습니다.
이 배경에는 연산 중 우발적인 오버플로우를 방지할 뿐만 아니라 부호성이 다른 오퍼랜드가 같은 식에 공존할 수 있도록 하는 것이 있습니다.
불행히도 암묵적인 타입 승격 규칙은 C언어의 가장 큰 결점 중 하나일 정도로 득보다 실이 훨씬 많습니다.이러한 규칙은 보통 C 프로그래머에 의해서도 잘 알려져 있지 않기 때문에 모든 종류의 매우 미묘한 버그를 일으킵니다.
일반적으로 프로그래머가 "x타입에 캐스팅만 하면 된다"고 말하는 시나리오를 볼 수 있지만, 그 이유는 알 수 없습니다.또는 이러한 버그는 겉으로 보기에는 단순하고 간단한 코드 안에서 발생하는 희귀하고 간헐적인 현상으로 나타납니다.C의 대부분의 비트 단위 연산자는 서명이 있는 오퍼랜드가 주어졌을 때 제대로 정의되지 않은 동작을 하기 때문에 암묵적인 프로모션은 비트를 조작하는 코드에서는 특히 문제가 됩니다.
정수형 및 변환 순위
C의 정수형은 다음과 같습니다.char
,short
,int
,long
,long long
그리고.enum
.
_Bool
/bool
형식 프로모션에 관해서는 정수형으로도 취급됩니다.
모든 정수는 지정된 변환 순위를 가집니다.C11 6.3.1.1, 가장 중요한 부분을 강조합니다.
모든 정수 유형에는 다음과 같이 정의된 정수 변환 순위가 있습니다.
: 두 개의 부호 있는 정수 타입이 같은 랭크일 수 없습니다.같은 랭크일 경우라도 마찬가지입니다.
: 부호 있는 정수형의 순위는 정밀도가 낮은 부호 있는 정수형의 순위보다 커야 합니다.
- 랭크long long int
계급 이상이어야 한다long int
이는 의 등급보다 커야 한다.int
이는 의 등급보다 커야 한다.short int
이는 의 등급보다 커야 한다.signed char
.
: 부호 없는 정수 타입의 랭킹은 대응하는 부호 있는 정수 타입의 랭킹과 같아야 합니다(있는 경우).
— 표준 정수 유형의 순위는 동일한 폭을 가진 확장 정수 유형의 순위보다 커야 합니다.
- char 순위는 부호 있는 char와 부호 없는 char의 랭크와 같다.
— _Bool의 순위는 다른 모든 표준 정수 유형의 순위보다 작아야 합니다.
- 열거된 유형의 순위는 호환되는 정수 유형의 순위와 같아야 합니다(6.7.2.2 참조).
의 종류stdint.h
여기서도 정렬할 수 있습니다.시스템상의 어떤 타입에 대응하고 있는 것과 같은 랭크입니다.예를들면,int32_t
과 같은 순위를 가지다int
32비트 시스템 상에서요.
또한 C11 6.3.1.1은 작은 정수형(공식 용어가 아님)으로 간주되는 유형을 지정합니다.
다음과 같은 표현은 다음과 같은 경우에 사용할 수 있습니다.
int
또는unsigned int
사용할 수 있습니다.: 정수 타입의 오브젝트 또는 식(외)
int
또는unsigned int
정수 변환 순위가 다음 등급 이하인 경우)int
그리고.unsigned int
.
이 다소 수수께끼 같은 텍스트가 실제로 의미하는 것은_Bool
,char
그리고.short
(또한int8_t
,uint8_t
etc)는 "작은 정수형"입니다.이들은 다음과 같이 특별한 방법으로 취급되며 암묵적인 프로모션을 받아야 합니다.
정수 프로모션
표현식에 작은 정수 유형이 사용될 때마다 암시적으로 다음과 같이 변환됩니다.int
항상 서명되어 있습니다.이를 정수 승격 또는 정수 승격 규칙이라고 합니다.
공식적으로 규칙에는 (C11 6.3.1.1)이 있습니다.
、 、
int
는 원래 수 됨). 이 은 "비트의 경우"로 됩니다.이 값은 다음과 같이 변환됩니다.int
그 이외의 는, 로 됩니다.; R/O/O/O/O/O는 R/O/O/O로 변환됩니다.unsigned int
이를 정수 프로모션이라고 합니다.
, 관계없이 으로 (서명된)됩니다.int
대부분의 표현에서 사용되는 경우.
이 텍스트는 종종 "모든 작은 부호 있는 정수형은 부호 있는 int로 변환되고 모든 작은 부호 없는 정수형은 부호 없는 int로 변환됩니다."로 오해됩니다.서 가 없는 '부호 없는 부분'이 '부호 없는 부분'이라는 없어요.unsigned short
및 「」의int
도 사이즈가 short
시스템 에서 '''는unsigned short
는 「피연산자」로 됩니다.unsigned int
에서와 같이 특별한 일은 일어나지 않습니다., 의 경우short
입니다.int
( (서명), (서명)으로int
쇼트가 서명되었거나 서명되지 않았거나 상관없습니다.
의 조작이 합니다. 예를 들어 C의 작은 에서는 C로 진행할 수 .char
★★★★★★★★★★★★★★★★★」short
에 int
을 사용하다
말도 안 되는 소리 같지만 다행히 컴파일러는 코드를 최적화할 수 있습니다. 2개의 「」, 「」를 .unsigned char
를 '피연산자'로 시킵니다.int
은 「 」로서 있습니다.int
그러나 컴파일러는 식을 최적화하여 예상대로 실제로 8비트 연산으로 실행할 수 있습니다.단, 여기서 문제가 발생합니다.컴파일러는 정수 프로모션에 의해 발생하는 시그니처의 암묵적인 변경을 최적화할 수 없습니다.왜냐하면 컴파일러는 프로그래머가 의도적으로 암묵적인 프로모션에 의존하고 있는지, 아니면 의도하지 않은 것인지 판단할 방법이 없기 때문입니다.
이것이 질문의 예 1이 실패하는 이유입니다.의 부호 없는 가 모두 유형으로 됩니다.int
int
및 의 입니다.x - y
은 「」입니다.int
-1
255
예상됐던 일이었습니다.는 8비트 명령이 할 수 .int
단, 서명 변경은 최적화되지 않을 수 있습니다.즉, 부정적인 결과를 초래하고, 결과적으로 이상한 수치를 낳는다는 것입니다.printf("%u
호출됩니다.예 1은 작업 결과를 다시 유형으로 캐스팅하여 수정할 수 있습니다.unsigned char
.
몇 가지 특별한 경우를 제외하고는++
그리고.sizeof
연산자, 정수 승진은 단항, 이진(또는 삼항) 연산자가 사용되는지 여부에 관계없이 C의 거의 모든 연산에 적용됩니다.
일반적인 산술 변환
C에서 바이너리 연산(2개의 오퍼랜드가 있는 연산)이 실행될 때마다 연산자의 양쪽 오퍼랜드가 동일한 유형이어야 합니다.따라서 오퍼랜드의 유형이 다른 경우 C는 한 오퍼랜드의 암묵적인 변환을 다른 오퍼랜드의 유형으로 적용합니다.이 작업을 수행하는 방법에 대한 규칙은 일반적인 아티메틱 변환(비공식적으로 "밸런싱"이라고도 함)이라고 불립니다.C11 6.3.18에 지정되어 있는 것은, 다음과 같습니다.
(이 규칙은 길고 중첩된 것으로 간주합니다.if-else if
를 참조하면, 보다 알기 쉬워집니다. : )
6.3.1.8 통상적인 산술 변환
연산 유형의 피연산자를 예상하는 많은 연산자는 유사한 방법으로 변환을 일으키고 결과 유형을 산출합니다.그 목적은 오퍼랜드와 결과의 공통 실유형을 결정하는 것입니다.지정된 오퍼랜드에 대해 각 오퍼랜드는 유형 도메인을 변경하지 않고 대응하는 실제 유형이 공통 실제 유형인 유형으로 변환됩니다.명시적으로 명기되어 있지 않는 한, 공통의 실수 타입은 결과의 대응하는 실수 타입이기도 합니다.그 결과, 피연산자가 같은 경우는 피연산자의 유형 도메인이며, 그렇지 않으면 복잡합니다.이 패턴을 통상의 산술 변환이라고 부릅니다.
- 첫째, 어느 피연산자의 대응하는 실제 유형이
long double
다른 피연산자는 타입 도메인을 변경하지 않고 대응하는 실제 타입이 다음과 같은 타입으로 변환됩니다.long double
.- 그 이외의 경우, 어느 피연산자의 대응하는 실제 타입이
double
다른 피연산자는 타입 도메인을 변경하지 않고 대응하는 실제 타입이 다음과 같은 타입으로 변환됩니다.double
.- 그 이외의 경우, 어느 피연산자의 대응하는 실제 타입이
float
다른 피연산자는 유형 도메인을 변경하지 않고 대응하는 실제 유형이 플로트인 유형으로 변환됩니다.그렇지 않으면 양쪽 오퍼랜드에서 정수 프로모션이 실행됩니다.다음으로 승격된 오퍼랜드에 다음 규칙이 적용됩니다.
- 두 오퍼랜드의 유형이 같은 경우 더 이상의 변환은 필요하지 않습니다.
- 그 이외의 경우, 양쪽 오퍼랜드에 부호 있는 정수 타입이 있는 경우 또는 부호 없는 정수 타입이 작은 오퍼랜드가 더 큰 랭크인 오퍼랜드 타입으로 변환됩니다.
- 그렇지 않으면 부호 없는 정수 유형을 가진 오퍼랜드의 순위가 다른 오퍼랜드 유형의 순위보다 크거나 같은 경우 부호 있는 정수 유형을 가진 오퍼랜드는 부호 없는 정수 유형을 가진 오퍼랜드의 유형으로 변환됩니다.
- 그 이외의 경우 부호 있는 정수형을 가진 오퍼랜드유형이 부호 없는 정수형을 가진 오퍼랜드유형의 모든 값을 나타낼 수 있는 경우 부호 없는 정수형을 가진 오퍼랜드유형은 부호 있는 정수형을 가진 오퍼랜드유형으로 변환됩니다.
- 그렇지 않으면 양쪽 오퍼랜드가 부호 있는 정수 타입을 가진 오퍼랜드의 유형에 대응하는 부호 없는 정수 타입으로 변환됩니다.
여기서 주목해야 할 점은 일반적인 산술 변환이 부동 소수점과 정수 변수 모두에 적용된다는 것입니다.정수의 경우 정수 승진은 일반적인 산술 변환 내에서 호출된다는 것도 알 수 있습니다.그리고 그 후, 두 오퍼랜드가 적어도 다음 순위의int
, 연산자는 같은 타입으로 같은 부호성을 가지는 밸런스를 취하고 있습니다.
이래서a + b
예 2에서 이상한 결과를 보여줍니다.두 피연산자는 정수이며 적어도 등급입니다.int
따라서 정수 프로모션은 적용되지 않습니다.오퍼랜드의 유형이 동일하지 않습니다.a
이unsigned int
그리고.b
이signed int
따라서 오퍼레이터는b
일시적으로 유형으로 변환됩니다.unsigned int
이 변환 중에는 부호 정보가 손실되어 큰 값이 됩니다.
유형을 다음으로 변경하는 이유short
예에서 3일이기 때문에 작업이 문제를 해결.short
있나 작은 정수 형식.두 피연산자 정수 형식으로 승진됨을 의미한다.int
서명된 있다.정수 승진한 후 두 피연산자( 같은 형식이 있습니다.int
)에는 추가적인 전환할 필요가 없다.예상대로 그리고 작업이 서명한 형식에 수행할 수 있다.
그 이전 포스트에 따르면, 나는 각각의 예에 대해 더 많은 정보를 주고자 한다.
예1)
int main(){
unsigned char x = 0;
unsigned char y = 1;
printf("%u\n", x - y);
printf("%d\n", x - y);
}
이후 부호 없는 int보다 작다, 우리는 그들을, 우리는(int)x-(int)y다))4294967295 정수 승진 및 부호 없는(int)())을 native())적용된다.
위의 코드:(same이 부진했다)에서 출력된.
4294967295
-1
그것을 고치는 데?
이전 게시글에서 추천한 대로 시도했지만 잘 되지 않습니다.다음은 이전 게시물을 기반으로 한 코드입니다.
그 중 하나를 부호 없는 int로 변경하다
int main(){
unsigned int x = 0;
unsigned char y = 1;
printf("%u\n", x - y);
printf("%d\n", x - y);
}
x는 이미 부호 없는 정수이므로 정수 프로모션을 y에만 적용합니다.그러면 (서명되지 않은 int)x-(int)y가 됩니다.여전히 같은 유형이 아니기 때문에 일반적인 산술 변환을 적용하면 (부호된 int)x-(부호된 int)y = 4294967295가 됩니다.
상기 코드의 출력은 다음과 같습니다(예상했던 것과 같음:
4294967295
-1
마찬가지로 다음 코드에서도 같은 결과가 나타납니다.
int main(){
unsigned char x = 0;
unsigned int y = 1;
printf("%u\n", x - y);
printf("%d\n", x - y);
}
둘 다 부호 없는 int로 변경하다
int main(){
unsigned int x = 0;
unsigned int y = 1;
printf("%u\n", x - y);
printf("%d\n", x - y);
}
둘 다 부호 없는 int이므로 정수 프로모션은 필요하지 않습니다.통상적인 산술 변환(같은 타입을 가진다)에 의해, (부호화된 int)x-(부호화된 int)y = 4294967295.
상기 코드의 출력은 다음과 같습니다(예상했던 것과 같음:
4294967295
-1
코드를 수정할 수 있는 방법 중 하나: (마지막에 유형 캐스트를 추가합니다.)
int main(){
unsigned char x = 0;
unsigned char y = 1;
printf("%u\n", x - y);
printf("%d\n", x - y);
unsigned char z = x-y;
printf("%u\n", z);
}
상기 코드의 출력은 다음과 같습니다.
4294967295
-1
255
예2)
int main(){
unsigned int a = 1;
signed int b = -2;
if(a + b > 0)
puts("-1 is larger than 0");
printf("%u\n", a+b);
}
둘 다 정수이기 때문에 정수 승격이 필요 없습니다.통상의 산술 변환에 의해, (부호 int)a+(부호 int)b = 1+4294967294 = 4294967295 가 됩니다.
상기 코드의 출력: (예상했던 것과 동일)
-1 is larger than 0
4294967295
어떻게 고치죠?
int main(){
unsigned int a = 1;
signed int b = -2;
signed int c = a+b;
if(c < 0)
puts("-1 is smaller than 0");
printf("%d\n", c);
}
상기 코드의 출력은 다음과 같습니다.
-1 is smaller than 0
-1
예 3)
int main(){
unsigned short a = 1;
signed short b = -2;
if(a + b < 0)
puts("-1 is smaller than 0");
printf("%d\n", a+b);
}
마지막 예에서는 정수 프로모션에 의해 a와 b가 모두 int로 변환되었기 때문에 문제가 수정되었습니다.
상기 코드의 출력은 다음과 같습니다.
-1 is smaller than 0
-1
만약 제가 어떤 컨셉을 혼동했다면 알려주세요.감사합니다.
언급URL : https://stackoverflow.com/questions/46073295/implicit-type-promotion-rules
'programing' 카테고리의 다른 글
Android 권한을 선언해도 작동하지 않음 (0) | 2022.08.28 |
---|---|
Vuex 저장소 변환에 어레이 커밋 (0) | 2022.08.24 |
hashCode에 소수점을 사용하는 이유는 무엇입니까? (0) | 2022.08.23 |
Java의 최종 블록에서 돌아오다 (0) | 2022.08.23 |
Passport를 사용하여 API 호출에서 laravel_token을 새로 고치는 방법 (0) | 2022.08.23 |