태그 보관물: type-conversion

type-conversion

C에서 부호없는 변환에 서명-항상 안전합니까? C 코드가 있다고 가정하십시오. unsigned int

다음 C 코드가 있다고 가정하십시오.

unsigned int u = 1234;
int i = -5678;

unsigned int result = u + i;

어떤 암시 적 변환이 진행되고 있으며이 코드는 u및의 모든 값에 대해 안전 i합니까? ( 이 예제의 결과 가 엄청난 양의 숫자로 오버플로 되지만 int로 다시 캐스팅 하고 실제 결과를 얻을 수 있다는 점에서 안전합니다.)



답변

짧은 답변

귀하가 i된다 변환 부가함으로써 부호없는 정수로 UINT_MAX + 1, 그 첨가는 큰 결과 부호없는 값으로 수행한다 result(의 값에 의존 u하고 i).

긴 답변

C99 표준에 따르면 :

6.3.1.8 일반적인 산술 변환

  1. 두 피연산자의 유형이 모두 같으면 더 이상 변환 할 필요가 없습니다.
  2. 그렇지 않으면 두 피연산자 모두 부호있는 정수 유형을 갖거나 둘 다 부호없는 정수 유형을 갖는 경우, 정수 변환 순위가 낮은 유형의 피연산자는 순위가 더 큰 피연산자의 유형으로 변환됩니다.
  3. 그렇지 않으면 부호없는 정수 유형을 가진 피연산자가 다른 피연산자의 유형 순위보다 크거나 같은 경우 부호있는 정수 유형을 가진 피연산자는 부호없는 정수 유형을 가진 피연산자의 유형으로 변환됩니다.
  4. 그렇지 않으면 부호있는 정수 유형의 피연산자 유형이 부호없는 정수 유형의 피연산자 유형의 모든 값을 나타낼 수 있으면 부호없는 정수 유형의 피연산자는 부호있는 정수 유형의 피연산자 유형으로 변환됩니다.
  5. 그렇지 않으면 두 피연산자가 부호있는 정수 유형의 피연산자 유형에 해당하는 부호없는 정수 유형으로 변환됩니다.

귀하의 경우, 서명되지 않은 int ( u) 및 signed int ( i)가 있습니다. 위의 (3)을 참조하면 두 피연산자가 모두 같은 순위를 가지므로 부호없는 정수 i변환 해야합니다 .

6.3.1.3 부호있는 정수와 부호없는 정수

  1. 정수 유형의 값이 _Bool 이외의 다른 정수 유형으로 변환 될 때 새 유형으로 값을 표시 할 수 있으면 변경되지 않습니다.
  2. 그렇지 않으면 새 유형에 부호가없는 경우 값이 새 유형의 범위에 올 때까지 새 유형에 표시 될 수있는 최대 값보다 하나 이상을 반복적으로 더하거나 빼서 값이 변환됩니다.
  3. 그렇지 않으면 새 유형이 서명되고 값을 표시 할 수 없습니다. 결과는 구현 정의되거나 구현 정의 신호가 발생합니다.

이제 위의 (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 표준에 의해 보장되지는 않으며 컴파일러 최적화는 보증하지 않는 가정을하는 코드를 혼란스럽게 만들 수 있습니다.


답변

성경을 참조 하십시오 :

  • 추가 작업으로 인해 int가 부호없는 int로 변환됩니다.
  • 2의 보수 표현과 동일한 크기의 유형을 가정하면 비트 패턴은 변경되지 않습니다.
  • unsigned int에서 signed int 로의 변환은 구현에 따라 다릅니다. (하지만 요즘 대부분의 플랫폼에서 예상대로 작동합니다.)
  • 크기가 다른 부호있는 부호와 부호없는 부호를 결합하는 경우 규칙이 조금 더 복잡합니다.

답변

하나의 부호없는 변수와 하나의 부호있는 변수가 추가되거나 모든 이진 연산이 암시 적으로 부호없는 것으로 변환되면 큰 결과가 발생합니다.

따라서 결과가 거대하고 잘못 될 수 있다는 점에서 안전하지만 결코 충돌하지는 않습니다.


답변

부호있는 것을 부호없는 것으로 변환 할 때 두 가지 가능성이 있습니다. 원래 양수인 숫자는 같은 값으로 유지됩니다 (또는 해석 된 것으로 간주 됨). 원래 음수 인 숫자는 이제 더 큰 양수로 해석됩니다.


답변

이전에 답변 한 것처럼 문제없이 서명 된 서명과 서명되지 않은 서명을주고받을 수 있습니다. 부호있는 정수의 경계는 -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