IT story

왜 0 <-0x80000000입니까?

hot-time 2020. 4. 4. 11:01
반응형

왜 0 <-0x80000000입니까?


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

#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 )은 항상 참입니다. 그게 어떻게 가능해?

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

#define INT32_MIN        (-2147483648L)

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


이것은 매우 미묘합니다.

프로그램의 모든 정수 리터럴에는 유형이 있습니다. 어떤 유형이 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 비트 시스템에서 리터럴 0x80000000은 유형 unsigned int입니다.

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

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

리터럴을 대체 할 때 2147483648L십진 표기법을 사용하므로 컴파일러는을 선택하지 않고 unsigned int대신에 맞 춥니 다 long. 또한 L 접미사는 long 가능 하면 원한다고 말합니다 . 6.4.4.1에서 언급 한 표를 계속 읽으면 L 접미사가 실제로 비슷한 규칙을 갖습니다 long.32 비트의 경우가 아닌 요청한 내부에 숫자가 맞지 않으면 컴파일러에서 해당 long long위치를 제공합니다 딱 맞습니다.


0x80000000unsigned값이 2147483648 리터럴입니다.

이에 대한 단항 마이너스를 적용하는 것은 여전히 당신에게 아닌 값을 가진 부호없는 형식을 제공합니다. 실제로 0이 아닌 값의 x경우 결국 값은 UINT_MAX - x + 1입니다.


이 정수 리터럴 0x80000000에는 유형이 unsigned int있습니다.

C 표준 (6.4.4.1 정수 상수)에 따름

5 정수 상수의 유형은 해당 값을 나타낼 수있는 해당 목록 중 첫 번째입니다.

그리고이 정수 상수는의 유형으로 나타낼 수 있습니다 unsigned int.

이 표현은

-0x80000000같은 unsigned int유형입니다. 또한 0x800000002의 보수 표현에서 다음과 같은 방법으로 계산하는 값이 같습니다.

-0x80000000 = ~0x80000000 + 1 => 0x7FFFFFFF + 1 => 0x80000000

예를 들어 작성하면 부작용이 있습니다.

int x = INT_MIN;
x = abs( x );

결과는 다시 나타납니다 INT_MIN.

따라서이 상태에서

bal < INT32_MIN

일반적인 산술 변환 규칙에 따라 long long int 유형으로 변환 된 부호없는과 비교 0됩니다 .0x80000000

0이보다 작은 것은 명백하다 0x80000000.


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

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

그래서 -0x80000000 == 0x80000000. 그리고 비교 는 (서명되지 (0 < 0x80000000)않았기 때문에 0x80000000) 사실입니다.


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

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

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

원시 꾸밈 숫자 상수는 긍정적이다.

이 소수 인 경우, 할당 된 유형을 개최 첫 번째 유형은 다음과 같습니다 int, long, long long.

상수 8 진수 또는 16 진수 인 경우, 그것을 보유하고있는 첫 번째 유형을 가져옵니다 int, unsigned, long, unsigned long, long long, unsigned long long.

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

-0x80000000또한 0이 아닌 값이고 부호없는 유형이므로 0보다 큽니다. 코드가이 을 a long long비교할 때 이 비교의 두 측면에서 변경되지 않으므로 0 < INT32_MINtrue입니다.


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

#define INT32_MIN        (-2147483647 - 1)

우리는 여기서 잠시 동안 환상의 땅에서 살펴 보겠습니다 intunsigned48 비트입니다.

그런 다음 유형에 0x80000000맞습니다 . 그런 다음 음수이고 출력 결과가 다릅니다.intint-0x80000000

[실제로 돌아 가기]

0x80000000부호있는 유형보다 some_signed_MAX아직 부호가없는 유형보다 부호가없는 유형에 맞기 때문에 some_unsigned_MAX부호없는 유형입니다.


C는 정수 리터럴 수 있다는 규칙 갖는다 signed또는 unsigned그에 부합 여부에 따라 signed또는 unsigned(정수 프로모션). A의 32비트 머신 리터럴이 0x80000000될 것입니다 unsigned. 2의 보수는 -0x80000000것입니다 0x8000000032 비트 컴퓨터에서. 따라서, 비교 bal < INT32_MIN사이 signedunsigned상기 C 규칙에 따라, 비교하기 전에 unsigned int변환 될 것이다 long long.

C11 : 6.3.1.8/1 :

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

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

참고 URL : https://stackoverflow.com/questions/34182672/why-is-0-0x80000000

반응형