서명되지 않은 정수 오버플로가 정의된 동작은 정의되지만 서명된 정수 오버플로는 정의되지 않는 이유는?
서명되지 않은 정수 오버플로는 C와 C++ 표준에 의해 잘 정의된다.예를 들어, C99 표준 (§6.2.5/9
) 상태
서명되지 않은 피연산자와 관련된 계산은 결과 서명되지 않은 정수 유형으로 나타낼 수 없는 결과가 결과 유형으로 나타낼 수 있는 가장 큰 값보다 큰 수치로 축소되기 때문에 절대 과소평가할 수 없다.
그러나 서명 정수 오버플로가 발생하는 두 표준 상태는 모두 정의되지 않은 동작이다.다시 C99 표에)§3.4.3/1
)
정의되지 않은 행동의 예는 정수 오버헤드에 대한 행동이다.
이러한 불일치에 대한 역사적 또는 (더 나은!) 기술적 이유가 있는가?
역사적 이유는 대부분의 C 구현자(컴파일러)가 사용했던 정수 표현으로 구현하기 가장 쉬운 오버플로 동작을 그냥 사용했기 때문이다.C 구현에서는 일반적으로 CPU에서 사용하는 것과 동일한 표현을 사용하므로 오버플로 동작은 CPU에서 사용하는 정수 표현에 따른다.
실제로 구현에 따라 달라질 수 있는 것은 서명된 가치에 대한 표현일 뿐이다: 한 사람의 보완, 두 사람의 보완, 사인 규모.서명되지 않은 유형의 경우 명백한 이항 표현(표준은 이항 표현만 허용)이 하나뿐이기 때문에 표준이 변동을 허용할 이유가 없다.
관련 인용문:
C99 6.2.6.1:3:
서명되지 않은 비트 필드 및 서명되지 않은 문자 형식의 객체에 저장된 값은 순수한 이진 표기법을 사용하여 표시해야 한다.
C99 6.2.6.2:2::
부호 비트가 하나일 경우, 값은 다음 방법 중 하나로 수정되어야 한다.
— 부호 비트 0의 해당 값이 부정(부호 및 크기)됨;
— 기호 비트의 값은 - (2)(2의 보완N)이다.
— 기호 비트의 값은 -(2N - 1)이다.
오늘날에는 모든 프로세서가 두 개의 보완 표현을 사용하지만, 서명된 산술 오버플로는 정의되지 않은 상태로 남아 있고 컴파일러 제조자들은 이러한 정의되지 않은 것을 최적화하기 위해 사용하기 때문에 정의되지 않은 상태로 유지되기를 원한다.예를 들어, 이안 랜스 테일러의 블로그 게시물이나 애그너 포그의 불평, 그리고 그의 버그 리포트에 대한 답을 보라.
Pascal의 좋은 대답(주요 동기가 확실시되는 것)과는 별도로, 일부 프로세서가 서명된 정수 오버플로에 예외를 야기할 가능성도 있으며, 이는 물론 컴파일러가 "다른 행동에 대한 접근"을 해야 하는 경우(예: 추가적인 지침을 사용하여 잠재적 오버플로를 확인하고 그 ca에서 다르게 계산함) 문제를 일으킬 수 있다.se)
"정의되지 않은 행동"이 "작동하지 않는다"는 것을 의미하지 않는다는 것 또한 주목할 필요가 있다.그 상황에서 원하는 것은 무엇이든 할 수 있도록 시행이 허용된다는 뜻이다.여기에는 "올바른 일"을 하는 것뿐만 아니라 "경찰을 불러" 또는 "충돌"을 하는 것도 포함된다.대부분의 컴파일러는, 가능하면, 그것을 정의하기 비교적 쉽다고 가정해, 「올바른 일을 하라」를 선택할 것이다(이 경우는, 그렇다).그러나 계산에서 오버플로가 발생할 경우 실제로 어떤 결과를 초래하는지, 컴파일러 MAL이 예상한 것 이외의 작업을 수행하는지 이해하는 것이 중요하다(그리고 이는 컴파일러 버전, 최적화 설정 등에 따라 매우 달라질 수 있음).
아마도 부호 없는 산수를 정의하는 또 다른 이유는 부호 없는 숫자가 정수 modulo 2^n을 형성하기 때문인데 여기서 n은 부호 없는 수의 너비일 것이다.서명되지 않은 숫자는 십진수 대신 이진수를 사용하여 나타내는 정수일 뿐이다.계량 시스템에서 표준 연산을 수행하는 것은 잘 이해된다.
OP의 인용문은 이러한 사실을 언급하지만, 또한 부호화되지 않은 정수를 이진수로 나타내는 단 하나의 명확하고 논리적인 방법만이 있다는 사실을 강조한다.이와는 대조적으로, 서명된 숫자는 대부분 두 개의 보완물을 사용하여 표현되지만 다른 선택은 표준에 설명된 대로 가능하다(섹션 6.2.6.2).
2의 보완 표현은 특정한 연산을 이진 형식으로 더 잘 이해할 수 있게 해준다.예를 들어, 음수의 증가는 양의 숫자에 대한 증가와 동일하다(과부하 조건에서 예상).기계 레벨의 일부 작동은 서명된 번호와 서명되지 않은 번호에 대해 동일할 수 있다.그러나 이러한 수술의 결과를 해석할 때, 일부 경우 - 양과 음의 오버플로 - 이치에 맞지 않는 경우가 있다.더욱이, 오버플로 결과는 서명된 기본 표현에 따라 다르다.
우선, C11 3.4.3은 모든 예시와 풋 노트와 마찬가지로 규범적인 텍스트가 아니므로 인용과는 관련이 없다는 점에 유의하십시오!
정수와 플로트의 오버플로우를 정의되지 않은 동작이라고 기술하는 관련 텍스트는 다음과 같다.
C11 6.5/5
식을 평가하는 동안 예외적인 조건(즉, 결과가 수학적으로 정의되지 않았거나 그 유형에 대해 표현 가능한 값의 범위에 있지 않은 경우)이 발생하는 경우, 행동은 정의되지 않는다.
서명되지 않은 정수 유형의 거동에 대한 자세한 내용은 여기에서 확인할 수 있다.
C11 6.2.5/9
부호화된 정수형의 비음수 값의 범위는 해당 부호 없는 정수형의 하위범위로, 각 유형에서 동일한 값의 표현이 동일하다.서명되지 않은 피연산자와 관련된 계산은 절대 오버플로가 될 수 없다. 그 결과 서명되지 않은 정수 유형으로 나타낼 수 있는 가장 큰 값보다 큰 수치로 축소되기 때문이다.
이것은 서명되지 않은 정수 타입을 특별한 케이스로 만든다.
또한 서명된 형식으로 변환되어 이전 값을 더 이상 나타낼 수 없는 경우에는 예외가 있다는 점에 유의하십시오.신호는 상승할 수 있지만, 그 동작은 구현에 의해 정의될 뿐이다.
C11 6.3.1.3
6.3.1.3 서명 및 서명되지 않은 정수
정수 타입의 값을 _Bool 이외의 정수 타입으로 변환할 때, 그 값을 새로운 타입으로 나타낼 수 있다면 변경되지 않는다.
그렇지 않으면, 새로운 타입이 서명되지 않은 경우, 그 값이 새로운 타입의 범위에 있을 때까지 새로운 타입으로 나타낼 수 있는 최대값보다 1을 더 가감하여 반복적으로 값을 환산한다.
그렇지 않으면 새로운 유형이 서명되고 그 값은 그 형태로 나타낼 수 없다. 결과가 구현 정의되거나 구현 정의 신호가 상승한다.
언급된 다른 문제 외에도, 서명되지 않은 수학 랩을 가지고 있는 것은 부호 없는 정수 유형이 추상 대수적 그룹(무엇보다, 어떤 값 쌍에 대해서도)으로 동작하게 한다.X
그리고Y
, 다른 가치들이 존재할 것이다.Z
그런X+Z
적절히 주조하면 동등하게 될 것이다.Y
그리고Y-Z
적절히 주조하면 동등하게 될 것이다.X
. 부호화되지 않은 값이 단지 저장 위치 형식일 뿐 중간 표현 형식은 아니었다면(예: 가장 큰 정수 형식과 동등한 부호화되지 않은 유형의 산술 연산이 처음 더 큰 부호화 유형으로 변환된 것처럼 행동했을 경우, 정의된 포장 동작의 필요성은 그만큼 크지 않을 것이다.그러나 가법 역이 없는 유형에서는 계산을 하기가 어렵다.
이것은 TCP 시퀀스 번호나 해시 계산과 같은 특정 알고리즘과 같이, 랩어라운드 동작이 실제로 유용한 상황에 도움이 된다.특히 계산에 가장 큰 정수 유형이 포함된 경우 계산을 수행하고 오버플로 여부를 확인하는 것이 사전에 오버플로 여부를 확인하는 것보다 쉽기 때문에 오버플로우를 감지해야 하는 상황에도 도움이 될 수 있다.
가장 기술적인 이유는 단순히 서명되지 않은 정수로 오버플로를 캡처하려고 하면 여러분으로부터 더 많은 움직이는 부품(예외 처리)과 프로세서(예외 던지기)가 필요하기 때문이다.
C와 C++는 서명된 정수를 사용하여 요구하지 않는 한 당신이 그것에 대해 지불하도록 하지 않을 것이다.이것은 마지막에서 볼 수 있듯이, 엄격한 규칙이 아니라, 서명되지 않은 정수에 대해 어떻게 진행되는가 하는 것이다.내 생각에, 이것은 서명된 정수를 서명되지 않은 것이 아니라 홀수원으로 만들지만 프로그래머가 여전히 과도하게 서명된 작업을 수행할 수 있기 때문에 이러한 근본적인 차이를 제공하는 것은 괜찮다.하지만 그렇게 하기 위해서는, 당신은 그것에 찬성해야 한다.
이유:
- 서명되지 않은 정수는 잘 정의된 오버플로 및 언더플로우
- 서명된 ->> 서명되지 않은 int의 깁스는 잘 정의되어 있다.
[uint's name]_MAX - 1
확장된 양의 수 범위로 매핑하기 위해 음수 값에 개념적으로 추가됨 - 서명되지 않은 -> 서명된 인트의 깁스는 잘 정의되어 있다.
[uint's name]_MAX - 1
서명된 유형의 최대값을 초과하는 양의 값에서 개념적으로 차감하여 음수로 매핑)
당신은 항상 잘 정의된 오버플로와 언더플로 동작으로 산술 연산을 수행할 수 있는데, 여기서 서명된 정수는 비록 둥글지만 먼저 부호 없는 정수에 캐스팅되었다가 다시 완성되면 다시 당신의 출발점이 된다.
int32_t x = 10;
int32_t y = -50;
// writes -60 into z, this is well defined
int32_t z = int32_t(uint32_t(y) - uint32_t(x));
CPU가 2의 칭찬을 사용하고 있다면(거의 모두 사용) 동일한 너비의 서명된 정수 유형과 부호 없는 정수 유형 사이의 캐스트는 무료다.어떤 이유로 당신이 목표로 하는 플랫폼이 서명된 정수에 2's Christ를 사용하지 않는다면, 당신은 uint32와 int32 사이에서 캐스팅할 때 약간의 전환 가격을 지불할 것이다.
그러나 int보다 작은 비트 너비를 사용할 때는 주의하십시오.
일반적으로 서명되지 않은 오버플로에 의존하는 경우, 8비트 또는 16비트라는 더 작은 단어를 사용하고 있다.이것들은 서명된 것으로 촉진될 것이다. int
모자의 한 방울에 (C는 완전히 미친 암묵적 정수 변환 규칙을 가지고 있다, 이것은 C의 가장 큰 숨겨진 gotcha 중 하나이다) 다음을 고려한다.
unsigned char a = 0;
unsigned char b = 1;
printf("%i", a - b); // outputs -1, not 255 as you'd expect
이를 피하기 위해서는 불필요하다고 생각하는 수술 중간이라도 그 타입의 너비에 의존하고 있을 때 항상 원하는 타입에 캐스팅해야 한다.이렇게 하면 임시방편을 캐스팅하여 서명을 받고 값을 잘라서 기대했던 것을 얻을 수 있다.거의 항상 자유롭게 캐스팅할 수 있고, 사실, 컴파일러는 여러분의 의도에 따라 더 적극적으로 최적화할 수 있기 때문에 그렇게 해 준 것에 대해 감사할 수도 있다.
unsigned char a = 0;
unsigned char b = 1;
printf("%i", (unsigned char)(a - b)); // cast turns -1 to 255, outputs 255
'programing' 카테고리의 다른 글
Vue js 호출 동작 내부 동작 (0) | 2022.05.02 |
---|---|
Java 파일 크기 가져오기 (0) | 2022.05.02 |
C에서 문자 숫자를 해당하는 정수로 변환 (0) | 2022.05.02 |
C에서 인쇄()와 스캔()를 위해 매번 데이터 유형을 지정해야 하는 이유는? (0) | 2022.05.02 |
VueJs + Element UI: photo el 선택으로 기본값을 설정하는 방법? (0) | 2022.05.02 |