답변:
짧은 답변
귀하가 i
된다 변환 부가함으로써 부호없는 정수로 UINT_MAX + 1
, 그 첨가는 큰 결과 부호없는 값으로 수행한다 result
(의 값에 의존 u
하고 i
).
긴 답변
C99 표준에 따르면 :
6.3.1.8 일반적인 산술 변환
- 두 피연산자의 유형이 모두 같으면 더 이상 변환 할 필요가 없습니다.
- 그렇지 않으면 두 피연산자 모두 부호있는 정수 유형을 갖거나 둘 다 부호없는 정수 유형을 갖는 경우, 정수 변환 순위가 낮은 유형의 피연산자는 순위가 더 큰 피연산자의 유형으로 변환됩니다.
- 그렇지 않으면 부호없는 정수 유형을 가진 피연산자가 다른 피연산자의 유형 순위보다 크거나 같은 경우 부호있는 정수 유형을 가진 피연산자는 부호없는 정수 유형을 가진 피연산자의 유형으로 변환됩니다.
- 그렇지 않으면 부호있는 정수 유형의 피연산자 유형이 부호없는 정수 유형의 피연산자 유형의 모든 값을 나타낼 수 있으면 부호없는 정수 유형의 피연산자는 부호있는 정수 유형의 피연산자 유형으로 변환됩니다.
- 그렇지 않으면 두 피연산자가 부호있는 정수 유형의 피연산자 유형에 해당하는 부호없는 정수 유형으로 변환됩니다.
귀하의 경우, 서명되지 않은 int ( u
) 및 signed int ( i
)가 있습니다. 위의 (3)을 참조하면 두 피연산자가 모두 같은 순위를 가지므로 부호없는 정수 i
로 변환 해야합니다 .
6.3.1.3 부호있는 정수와 부호없는 정수
- 정수 유형의 값이 _Bool 이외의 다른 정수 유형으로 변환 될 때 새 유형으로 값을 표시 할 수 있으면 변경되지 않습니다.
- 그렇지 않으면 새 유형에 부호가없는 경우 값이 새 유형의 범위에 올 때까지 새 유형에 표시 될 수있는 최대 값보다 하나 이상을 반복적으로 더하거나 빼서 값이 변환됩니다.
- 그렇지 않으면 새 유형이 서명되고 값을 표시 할 수 없습니다. 결과는 구현 정의되거나 구현 정의 신호가 발생합니다.
이제 위의 (2)를 참조해야합니다. 귀하는 i
추가하여 부호없는 값으로 변환됩니다 UINT_MAX + 1
. 따라서 결과는 UINT_MAX
구현 에 어떻게 정의되어 있는지에 달려 있습니다. 크기는 크지 만 오버플로는 발생하지 않습니다.
6.2.5 (9)
부호없는 피연산자가 포함 된 계산은 결과 부호없는 정수 유형으로 표현할 수없는 결과가 결과 유형으로 표현할 수있는 가장 큰 값보다 1이 큰 모듈로 감소되므로 오버 플로우 할 수 없습니다.
보너스 : 산술 변환 Semi-WTF
#include <stdio.h>
int main(void)
{
unsigned int plus_one = 1;
int minus_one = -1;
if(plus_one < minus_one)
printf("1 < -1");
else
printf("boring");
return 0;
}
이 링크를 사용하여 온라인으로 시도 할 수 있습니다 : https://repl.it/repls/QuickWhimsicalBytes
보너스 : 산술 변환 부작용
산술 변환 규칙을 사용 UINT_MAX
하여 부호없는 값을로 초기화 하여 값을 얻을 수 있습니다 -1
.
unsigned int umax = -1; // umax set to UINT_MAX
위에서 설명한 변환 규칙으로 인해 시스템의 부호있는 숫자 표시에 관계없이 이식 가능합니다. 자세한 내용은이 SO 질문을 참조하십시오. -1을 사용하여 모든 비트를 true로 설정하는 것이 안전합니까?
부호있는 부호를 부호없는 부호로 변환한다고해서 반드시 부호있는 값의 표현을 복사하거나 해석하는 것은 아닙니다 . C 표준 인용 (C99 6.3.1.3) :
정수 유형의 값이 _Bool 이외의 다른 정수 유형으로 변환 될 때 새 유형으로 값을 표시 할 수 있으면 변경되지 않습니다.
그렇지 않으면 새 유형에 부호가없는 경우 값이 새 유형의 범위에 올 때까지 새 유형에 표시 될 수있는 최대 값보다 하나 이상을 반복적으로 더하거나 빼서 값이 변환됩니다.
그렇지 않으면 새 유형이 서명되고 값을 표시 할 수 없습니다. 결과는 구현 정의되거나 구현 정의 신호가 발생합니다.
요즘 거의 보편적 인 2의 보수 표현의 경우 규칙은 비트를 해석하는 것과 일치합니다. 그러나 다른 표현 (부호 및 크기 또는 1의 보수)의 경우 C 구현은 여전히 동일한 결과를 정렬해야하므로 변환이 비트를 복사 할 수는 없습니다. 예를 들어, 표현에 관계없이 (부호없는) -1 == UINT_MAX입니다.
일반적으로 C의 변환은 표현이 아닌 값에서 작동하도록 정의됩니다.
원래 질문에 대답하려면 :
unsigned int u = 1234;
int i = -5678;
unsigned int result = u + i;
i 값은 unsigned int로 변환되어을 산출 UINT_MAX + 1 - 5678
합니다. 그런 다음이 값은 부호없는 값 1234에 추가되어을 산출 UINT_MAX + 1 - 4444
합니다.
부호없는 오버플로와 달리 부호있는 오버플로는 정의되지 않은 동작을 호출합니다. 랩 어라운드가 일반적이지만 C 표준에 의해 보장되지는 않으며 컴파일러 최적화는 보증하지 않는 가정을하는 코드를 혼란스럽게 만들 수 있습니다.
이전에 답변 한 것처럼 문제없이 서명 된 서명과 서명되지 않은 서명을주고받을 수 있습니다. 부호있는 정수의 경계는 -1 (0xFFFFFFFF)입니다. 그것에서 더하기와 빼기를 시도해보십시오. 당신은 그것을 되돌릴 수 있고 올바른 것을 알 수 있습니다.
그러나 앞뒤로 캐스팅하려는 경우 변수의 이름을 명확하게 지정하여 변수의 이름을 지정하는 것이 좋습니다.
int iValue, iResult;
unsigned int uValue, uResult;
더 중요한 문제에 산만 해져 힌트없이 이름이 지정된 변수가 어떤 유형인지 잊어 버리는 것은 너무 쉽습니다. 부호없는 것으로 캐스팅하고 배열 인덱스로 사용하고 싶지 않습니다.
어떤 암묵적 전환이 일어나고 있습니까?
나는 부호없는 정수로 변환됩니다.
이 코드는 u와 i의 모든 값에 안전합니까?
잘 정의 된 의미에서 안전합니다 ( https://stackoverflow.com/a/50632/5083516 참조 ).
규칙은 일반적으로 읽기 어려운 표준 언어로 작성되지만 부호있는 정수에 사용 된 표현은 부호없는 정수에 2의 보수 표현이 포함됩니다.
더하기, 빼기 및 곱하기는이 숫자에서 올바르게 작동하여 "실제 결과"를 나타내는 2의 보수 숫자를 포함하는 부호없는 다른 정수를 만듭니다.
더 큰 부호없는 정수 유형으로 나누기와 캐스트는 잘 정의 된 결과를 갖지만 그 결과는 "실제 결과"의 2의 보수 표현이 아닙니다.
(이 예제의 결과가 엄청난 양의 숫자로 오버플로 될지라도 int로 다시 캐스팅하여 실제 결과를 얻을 수 있다는 점에서 안전합니다.)
부호있는 부호에서 부호없는 것으로의 변환은 표준에 의해 정의되지만 그 반대는 구현 정의 gcc와 msvc는 부호없는 정수에 저장된 2의 보수를 부호있는 정수로 다시 변환 할 때 "실제 결과"를 얻도록 변환을 정의합니다 . 부호있는 정수에 2의 보수를 사용하지 않는 모호한 시스템에서만 다른 동작을 찾을 것으로 기대합니다.
https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation https://msdn.microsoft.com/en-us/library/0eex498h.aspx
끔찍한 답변
오즈 구르 오시 치탁
부호가있는 부호에서 부호가없는 부호로 (또는 그 반대로) 변환 할 때 번호의 내부 표현은 변경되지 않습니다. 변경 사항은 컴파일러가 부호 비트를 해석하는 방법입니다.
이것은 완전히 잘못되었습니다.
매트 프레드릭 손
하나의 부호없는 변수와 하나의 부호있는 변수가 추가되거나 모든 이진 연산이 암시 적으로 부호없는 것으로 변환되면 큰 결과가 발생합니다.
이것도 잘못되었습니다. 부호없는 정수는 부호없는 유형의 패딩 비트로 인해 동일한 정밀도를 가지면 정수로 승격 될 수 있습니다.
smh
추가 작업으로 인해 int가 부호없는 int로 변환됩니다.
잘못된. 어쩌면 그렇지 않을 수도 있습니다.
unsigned int에서 signed int 로의 변환은 구현에 따라 다릅니다. (하지만 요즘 대부분의 플랫폼에서 예상대로 작동합니다.)
잘못된. 오버플로가 발생하거나 값이 유지되면 정의되지 않은 동작입니다.
익명
i의 값은 unsigned int로 변환됩니다 ...
잘못된. 부호없는 int에 상대적인 int의 정밀도에 따라 다릅니다.
테일러 가격
이전에 답변 한 것처럼 문제없이 서명 된 서명과 서명되지 않은 서명을주고받을 수 있습니다.
잘못된. 부호있는 정수 범위 밖의 값을 저장하려고하면 정의되지 않은 동작이 발생합니다.
이제 마지막으로 질문에 대답 할 수 있습니다.
int의 정밀도가 unsigned int와 같으면 u는 부호있는 int로 승격되고 식 (u + i)에서 -4444 값을 얻습니다. 이제 u와 i에 다른 값이 있으면 오버플로와 정의되지 않은 동작이 발생할 수 있지만 정확한 숫자를 사용하면 -4444 [1]이 됩니다. 이 값은 int 유형입니다. 그러나 그 값을 부호없는 int에 저장하려고하면 부호없는 int로 캐스팅되고 결과는 (UINT_MAX + 1)-4444가됩니다.
부호없는 int의 정밀도가 int의 정밀도보다 클 경우 부호있는 int는 부호없는 int로 승격되어 값 (UINT_MAX + 1)-5678을 산출하고 다른 부호없는 int 1234에 추가됩니다. u와 i가 있어야합니다. 식이 {0..UINT_MAX} 범위를 벗어나는 다른 값은 결과 DOES가 {0..UINT_MAX 범위 내에있을 때까지 값 (UINT_MAX + 1)을 더하거나 빼고 정의되지 않은 동작이 발생하지 않습니다. .
정밀도는 무엇입니까?
정수에는 패딩 비트, 부호 비트 및 값 비트가 있습니다. 부호없는 정수에는 분명히 부호 비트가 없습니다. 부호없는 문자는 패딩 비트가 없도록 보장됩니다. 정수가 갖는 값의 비트 수는 정밀도가 얼마나되는지입니다.
[Gotchas]
패딩 비트가 존재하는 경우 매크로 단독의 매크로 크기는 정수의 정밀도를 결정하는 데 사용될 수 없습니다. 그리고 바이트의 크기는 C99에 의해 정의 된 8 진수 (8 비트) 일 필요는 없습니다.
[1] 두 지점 중 하나에서 오버플로가 발생할 수 있습니다. 추가하기 전에 (프로모션 중)-서명되지 않은 int가있는 경우 int에 맞지 않습니다. 부호없는 int가 int 범위 내에 있더라도 추가 후 오버플로가 발생할 수 있으며 추가 후에도 여전히 오버플로가 발생할 수 있습니다.
int
변환되는 것을 보증합니다 unsigned int
.
int
또는 unsigned int
유형이 unsigned int
있거나 int
예상되는 유형 중 하나로 변환하는 데 사용됩니다 . 은 "또는 동등"은 변환 계수의 열거 된 종류를 허용 TC2 첨가 하였다는 동일 int
또는 unsigned int
해당 유형 중 하나로 변환한다. 그것은 승진 사이의 변환 것이라고 설명한다는 계획이없는 unsigned int
및 int
. 사이의 일반적인 유형 결정 unsigned int
과는 int
여전히 6.3.1.8, 심지어 포스트 TC2의 적용을받습니다.