왜 0 <-0x80000000입니까?


253

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

#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)

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


3
얼마 CHAR_BIT * sizeof(int)입니까?
5gon12eder

1
bal을 인쇄 해 보셨습니까?
Ryan Fitzpatrick

10
이럴 더 재미있는 점은 사실이다 만을 위해 -0x80000000,하지만에 대한 거짓 -0x80000000L, -2147483648그리고 -2147483648L왜 INT 리터럴 : (GCC 4.1.2), 문제는 그래서 -0x80000000INT의 문자와 다른가 -2147483648?
Andreas Fester

2
@Bathsheba 난 그냥 온라인 컴파일러에서 프로그램을 실행 tutorialspoint.com/codingground.htm
Jayesh Bhoi에게

2
(일부 화신) <limits.h>이로 정의 INT_MIN되어 있음을 알게 된 경우 (-2147483647 - 1), 이제 그 이유를 알 수 있습니다.
zwol

답변:


363

이것은 매우 미묘합니다.

프로그램의 모든 정수 리터럴에는 유형이 있습니다. 어떤 유형이 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위치를 제공합니다 딱 맞습니다.


3
"... 리터럴을 -2147483648L로 바꾸면 명시 적으로 길어지고 서명됩니다." 음, 32 비트에서 long시스템 2147483648L하는에 맞지 않는 long이되고, 그래서 long long, 다음-정도 나는 생각했다 - 적용된다.
chux-Reinstate Monica

2
@ASH 따라서 int가 가질 수있는 최대 수는입니다 0x7FFFFFFF. 직접 해보십시오 :#include <limits.h> printf("%X\n", INT_MAX);
Lundin

5
@ASH 소스 코드에서 정수 리터럴의 16 진 표현을 부호있는 숫자의 기본 2 진 표현과 혼동하지 마십시오. 0x7FFFFFFF소스 코드로 작성된 리터럴 은 항상 양수이지만 int변수에는 0xFFFFFFFF 값까지 원시 이진 숫자가 포함될 수 있습니다.
Lundin

2
@ASH ìnt n = 0x80000000는 부호없는 리터럴에서 부호있는 유형으로 변환합니다. 일어날 일은 컴파일러에 달려 있습니다-그것은 구현 정의 동작입니다. 이 경우 전체 리터럴을로 표시 int하여 부호 비트를 덮어 씁니다. 다른 시스템에서는 유형을 표현할 수 없으며 정의되지 않은 동작을 호출하면 프로그램이 중단 될 수 있습니다. int n=2147483648;그렇게하면 16 진수 표기법과 전혀 관련이없는 것과 동일한 동작을 얻을 수 있습니다.
Lundin

3
-부호없는 정수에 단항 이 적용되는 방법에 대한 설명은 약간 확장 될 수 있습니다. 서명되지 않은 값은 서명 된 값으로 "승격"되거나 결과가 정의되지 않을 것이라고 항상 가정했습니다 (다행히 가정에 의존하지는 않았지만). (정직하게, 그것은 컴파일 오류가되어야한다; 무엇 - 3u을 의미 하는가?)
Kyle Strand

27

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

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


23

이 정수 리터럴 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.


12

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

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

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


이것은 32 비트라고 가정합니다 int. 그것이 매우 일반적인 선택이지만, 주어진 구현에서 int더 좁거나 넓을 수 있습니다. 그러나이 경우에 대한 올바른 분석입니다.
John Bollinger

이것은 OP의 코드와 관련이 없으며 -0x80000000부호없는 산술입니다. ~0x800000000다른 코드입니다.
MM

이것은 단순히 나에게 가장 좋고 올바른 대답 인 것 같습니다. @MM 그는 2의 보수를 취하는 방법을 설명하고 있습니다. 이 답변은 음수 부호가 숫자에 대해 수행하는 작업을 구체적으로 설명합니다.
Octopus

@Octopus 음수 부호는 숫자에 2의 보수를 적용 하지 않습니다 (!) 이것은 분명해 보이지만 코드에서 일어나는 일을 설명하지는 않습니다 -0x80000000! 실제로 2의 보수는이 질문과는 전혀 관련이 없습니다.
MM

12

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

아래 코드 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부호없는 유형입니다.


8

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

C11 : 6.3.1.8/1 :

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

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.