c - 프레스 - 왜 0<-0x80000000입니까?




워드 프레스 이미지 seo (4)

아래에 간단한 프로그램이 있습니다.

#include <stdio.h>

#define INT32_MIN        (-0x80000000)

int main(void) 
{
    long long bal = 0;

    if(bal < INT32_MIN )
    {
        printf("Failed!!!");
    }
    else
    {
        printf("Success!!!");
    }
    return 0;
}

if(bal < INT32_MIN ) 조건은 항상 true입니다. 그게 어떻게 가능해?

매크로를 다음과 같이 변경하면 정상적으로 작동합니다.

#define INT32_MIN        (-2147483648L)

누구든지 문제를 지적 할 수 있습니까?


C는 정수 리터럴이 signed 있거나 unsigned 규칙에 따라 signed 있거나 unsigned 가 없는지 여부에 따라 달라집니다 (정수 승격). 32 비트 시스템에서 리터럴 0x80000000unsigned 가 없습니다. 32 비트 시스템에서 -0x80000000 의 2의 보수는 0x80000000 입니다. 따라서 비교 bal < INT32_MINsignedunsigned 사이이며 C 규칙에 따라 비교하기 전에 unsigned intlong long 으로 변환됩니다.

C11 : 6.3.1.8/1 :

[...] 그렇지 않으면 부호있는 정수 유형의 피연산자 유형이 부호없는 정수 유형의 피연산자 유형의 모든 값을 나타낼 수 있으면 부호없는 정수 유형의 피연산자는 다음을 사용하여 피연산자의 유형으로 변환됩니다. 부호있는 정수 유형.

따라서 bal < INT32_MIN 은 항상 true 입니다.


숫자 상수 0x80000000unsigned int 유형입니다. 우리가 -0x80000000 을 취하고 -0x80000000 2s 칭찬 수학을하면, 우리는 이것을 얻습니다 :

~0x80000000 = 0x7FFFFFFF
0x7FFFFFFF + 1 = 0x80000000

따라서 -0x80000000 == 0x80000000 입니다. 그리고 (0 < 0x80000000) 비교하는 것은 (0 < 0x80000000) 0x80000000 이 부호가 없으므로) 사실입니다.


이것은 매우 미묘합니다.

프로그램의 모든 정수 리터럴에는 유형이 있습니다. 어떤 유형이 6.4.4.1의 테이블에 의해 규제됩니다.

Suffix      Decimal Constant    Octal or Hexadecimal Constant

none        int                 int
            long int            unsigned int
            long long int       long int
                                unsigned long int
                                long long int
                                unsigned long long int

리터럴 숫자가 기본 int 유형에 맞지 않으면 위 표에 표시된대로 다음 큰 유형을 시도합니다. 따라서 일반 십진 정수 리터럴의 경우 다음과 같습니다.

  • int 보십시오
  • 맞지 않으면 long 시도하십시오
  • 맞지 않으면 long long 시도하십시오.

16 진 리터럴은 다르게 동작합니다! 리터럴이 int 와 같은 부호있는 유형에 맞지 않으면 먼저 더 큰 유형을 시도하기 전에 unsigned int 를 시도합니다. 위 표의 차이점을 참조하십시오.

따라서 32 비트 시스템에서 리터럴 0x80000000unsigned int 유형입니다.

이것은 부호있는 정수를 오버플로 할 때와 달리 구현 정의 동작을 호출하지 않고 리터럴에 단항 연산자를 적용 할 수 있음을 의미합니다. 대신 양수 값인 0x80000000 값을 얻습니다.

bal < INT32_MIN 은 일반적인 산술 변환을 호출하고 식 0x80000000 의 결과는 unsigned int 에서 long long 승격됩니다. 0x80000000 값은 유지되고 0은 0x80000000보다 작으므로 결과입니다.

리터럴을 2147483648L2147483648L 십진 표기법을 사용하므로 컴파일러는 unsigned int 선택하지 않고 long 안에 넣습니다. 또한 L 접미사는 가능 하면 long 원한다고 말합니다. 6.4.4.1에서 언급 한 표를 계속 읽으면 L 접미사가 실제로 비슷한 규칙을 갖습니다 .32 비트의 경우가 아닌 요청 된 long 안에 숫자가 맞지 않으면 컴파일러에서 long long 값을 제공합니다 딱 잘 맞을 것입니다.


- 가 숫자 상수의 일부라고 생각하면 혼란이 발생합니다.

아래 코드에서 0x80000000 은 숫자 상수입니다. 그 유형은 그에 대해서만 결정됩니다. - 는 나중에 적용 되며 유형을 변경하지 않습니다 .

#define INT32_MIN        (-0x80000000)
long long bal = 0;
if (bal < INT32_MIN )

미공개 숫자 상수는 양수입니다.

10 진수 인 경우 할당 된 유형은 첫 번째 유형 인 int , long , long long 입니다.

상수가 8 진수 또는 16 진수 인 경우 정수를 보유한 첫 번째 유형 ( int , unsigned , long , unsigned long , long long , unsigned long long 가져옵니다.

OP의 시스템에서 0x80000000unsigned 또는 unsigned long 유형을 가져옵니다. 어느 쪽이든, 그것은 부호없는 유형입니다.

-0x80000000 은 0이 아닌 값이고 부호없는 유형이므로 0보다 큽니다. 코드가 long long 과 비교할 때 이 비교의 양쪽에서 변경되지 않으므로 0 < INT32_MIN 은 true입니다.

대체 정의는이 이상한 행동을 피합니다

#define INT32_MIN        (-2147483647 - 1)

intunsigned 가 48 비트 인 판타지 랜드에서 잠시 걸을 수 있습니다.

그런 다음 0x80000000int 적합하며 int 유형입니다. 그런 다음 -0x80000000 은 음수이며 인쇄 결과가 다릅니다.

[실제로 돌아 가기]

0x80000000some_signed_MAX 보다 크지 만 some_signed_MAX 내에 있기 때문에 부호없는 유형 앞에 부호없는 유형에 맞기 때문에 부호없는 유형입니다.





numeric-conversion