source

C에서 서명되지 않은 변환으로 서명됨 - 항상 안전한가?

factcode 2022. 7. 21. 23:32
반응형

C에서 서명되지 않은 변환으로 서명됨 - 항상 안전한가?

다음과 같은 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 )를 입력하고 i(3)을에, 「(3)」, 「(3)」, 「」, 「(3)」, 「(3)」, 「(3)」, 「(3)」, 「」, 「(3)」, 「(3)」,i부호 없는 정수로 변환해야 합니다.

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

  1. 정수 타입의 값이 _Bool 이외의 정수 타입으로 변환되었을 때 값을 새로운 타입으로 나타낼 수 있는 경우에는 변경되지 않습니다.
  2. 그렇지 않으면 새 유형이 부호 없는 경우 값이 새 유형 범위 내에 있을 때까지 새 유형으로 나타낼 수 있는 최대 값보다 하나 더 많이 추가 또는 빼기를 반복하여 값이 변환됩니다.
  3. 그렇지 않으면 새로운 유형이 서명되어 값이 그 유형에 표시될 수 없습니다.결과가 구현 정의되거나 구현 정의 신호가 발생합니다.

이제 위의 (2)를 참조할 필요가 있습니다.의 ★★★★★★★★★★★★★★★★★.i는 부호 됩니다.UINT_MAX + 1 하느냐에 수 UINT_MAX는 구현에 정의되어 있습니다.크기는 크지만, 다음과 같은 이유로 넘치지는 않습니다.

6.2.5 (9)

부호 없는 정수 타입으로 나타낼 수 없는 결과는 결과 타입으로 나타낼 수 있는 최대값보다 1 큰 수치로 감소하기 때문에 부호 없는 오퍼랜드를 포함하는 계산은 결코 오버플로할 수 없습니다.

보너스: 산술 변환 준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 이외의 정수 타입으로 변환되었을 때 값을 새로운 타입으로 나타낼 수 있는 경우에는 변경되지 않습니다.

그렇지 않으면 새 유형이 부호 없는 경우 값이 새 유형 범위 내에 있을 때까지 새 유형으로 나타낼 수 있는 최대 값보다 하나 더 많이 추가 또는 빼기를 반복하여 값이 변환됩니다.

그렇지 않으면 새로운 유형이 서명되어 값이 그 유형에 표시될 수 없습니다.결과가 구현 정의되거나 구현 정의 신호가 발생합니다.

오늘날 거의 보편화된 두 개의 보완 표현에 대해 규칙은 비트를 재해석하는 것과 일치합니다.그러나 다른 표현(부호 및 크기 또는 보형)의 경우, C 구현은 여전히 동일한 결과를 준비해야 합니다. 즉, 변환은 단순히 비트를 복사할 수 없습니다.예를 들어 (지정 해제)-1 == UINT_MAX는 표현에 관계없이 지정합니다.

일반적으로 C의 변환은 표현이 아닌 값으로 동작하도록 정의됩니다.

원래 질문에 답하려면:

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

unsigned int result = u + i;

은 부호 int로 가 생성됩니다.UINT_MAX + 1 - 5678은 부호 값 되어 1234가 됩니다.UINT_MAX + 1 - 4444.

(서명되지 않은 오버플로와 달리 서명된 오버플로우는 정의되지 않은 동작을 호출합니다.랩어라운드는 일반적이지만 C 표준에서는 보증되지 않습니다.또한 컴파일러 최적화는 보증되지 않은 가정을 하는 코드에 큰 피해를 줄 수 있습니다.)

C 프로그래밍 언어, 제2판 (ISBN 0131103628) 참조

  • 추가 조작에 의해 int는 부호 없는 int로 변환됩니다.
  • 2개의 보완 표현과 같은 크기의 타입이라고 가정하면 비트 패턴은 변경되지 않습니다.
  • unsigned int에서 signed int로의 변환은 구현에 의존합니다(그러나 현재 대부분의 플랫폼에서 예상대로 동작하고 있습니다).
  • 크기가 다른 서명과 부호를 조합하는 경우에는 규칙이 조금 더 복잡합니다.

하나의 부호 없는 변수와 하나의 부호 있는 변수가 추가되면(또는 임의의 바이너리 연산), 두 변수 모두 암묵적으로 부호 없는 변수로 변환되므로 이 경우 큰 결과가 발생합니다.

따라서 결과는 크고 틀릴 수 있지만 결코 무너지지 않는다는 점에서 안전하다.

서명된 상태에서 서명되지 않은 상태로 변환할 경우 두 가지 가능성이 있습니다.원래 양수였던 숫자는 동일한 값으로 유지됩니다(또는 로 해석됩니다).원래 음수였던 숫자는 이제 더 큰 양의 숫자로 해석됩니다.

끔찍한 대답 갤로어

오즈구르 오즈시타크

부호 첨부에서 부호 없음(및 그 반대)으로 캐스팅해도 번호의 내부 표현은 변경되지 않습니다.다른 점은 컴파일러가 부호 비트를 해석하는 방법입니다.

이건 완전히 잘못됐어.

매츠 프레드릭슨

하나의 부호 없는 변수와 하나의 부호 있는 변수가 추가되면(또는 임의의 바이너리 연산), 두 변수 모두 암묵적으로 부호 없는 변수로 변환되므로 이 경우 큰 결과가 발생합니다.

이것도 틀렸어요.부호 없는 유형의 패딩 비트에 의해 부호 없는 int는 동일한 정밀도를 가질 경우 int로 승격할 수 있습니다.

스마

추가 조작에 의해 int는 부호 없는 int로 변환됩니다.

틀렸어 그럴 수도 있고 아닐 수도 있어

unsigned int에서 signed int로의 변환은 구현에 의존합니다(그러나 현재 대부분의 플랫폼에서 예상대로 동작하고 있습니다).

틀렸습니다. 오버플로를 일으키거나 값이 보존되는 경우 정의되지 않은 동작입니다.

익명

i 값은 부호 없는 int로 변환됩니다.

틀렸어 서명되지 않은 int에 대한 int의 정밀도에 달려있어

테일러 프라이스

앞서 답변한 바와 같이 서명된 것과 서명되지 않은 것 사이를 문제없이 오갈 수 있습니다.

틀렸습니다. 부호 있는 정수 범위를 벗어난 값을 저장하려고 하면 정의되지 않은 동작이 발생합니다.

이제서야 질문에 대답할 수 있어요.

int의 정밀도가 unsigned int와 같으면 u는 부호 있는 int로 승격되고 식(u+i)에서 -4444 값을 얻을 수 있습니다.이제 u와 나의 값이 다른 경우 오버플로우 및 정의되지 않은 동작이 발생할 수 있지만 정확한 수치를 사용하면 -4444 [1]이 됩니다.이 값의 유형은 int입니다.단, 이 값을 부호 없는 int에 저장하려고 합니다.그러면 부호 없는 int에 캐스트되어 결과적으로 (UNT_MAX+1) - 4444가 됩니다.

unsigned int의 정밀도가 int의 정밀도보다 클 경우 int는 int의 값(UNT_MAX+1) - 5678을 생성하는 int로 승격되어 다른 int 1234에 추가됩니다.표현식이 {0} 범위를 벗어나도록 u와 i의 값이 다른 경우.UINT_MAX} 결과 값이 {0} 범위에 포함될 때까지 값(UNT_MAX+1)이 추가 또는 감산됩니다.UINT_MAX) 및 정의되지 않은 동작은 발생하지 않습니다.

정밀도란 무엇인가?

정수에는 패딩 비트, 부호 비트 및 값 비트가 있습니다.부호 없는 정수에는 분명히 부호 비트가 없습니다.부호 없는 문자에는 패딩 비트가 없는 것이 더욱 보증됩니다.정수의 비트 수는 정수의 정밀도입니다.

[당황]

패딩 비트가 존재하는 경우 매크로 크기만으로는 정수의 정밀도를 판단할 수 없습니다.또한 바이트의 크기는 C99에서 정의된 옥텟(8비트)일 필요는 없습니다.

[1] 오버플로는 두 지점 중 하나에서 발생할 수 있습니다.추가 전(프로모션 중) - 서명되지 않은 int가 너무 커서 int에 들어가지 않는 경우.또한 서명되지 않은 int가 int의 범위 내에 있더라도 추가 후에도 오버플로가 발생할 수 있습니다.

앞서 답변한 바와 같이 서명된 것과 서명되지 않은 것 사이를 문제없이 오갈 수 있습니다.부호 있는 정수의 경계 대소문자는 -1(0xFFFFFF)입니다.덧셈과 뺄셈을 해보면 다시 던져서 맞힐 수 있다는 것을 알 수 있어요.

단, 앞뒤로 캐스트할 경우에는 변수의 유형을 명확하게 할 수 있도록 변수의 이름을 지정할 것을 강력히 권장합니다.예를 들어 다음과 같습니다.

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

언급URL : https://stackoverflow.com/questions/50605/signed-to-unsigned-conversion-in-c-is-it-always-safe

반응형