IT TIP

형식 변환-부호없는 int / char로

itqueen 2020. 10. 23. 19:50
반응형

형식 변환-부호없는 int / char로


아래 프로그램을 실행하려고했습니다.

#include <stdio.h>

int main() {
    signed char a = -5;
    unsigned char b = -5;
    int c = -5;
    unsigned int d = -5;

    if (a == b)
        printf("\r\n char is SAME!!!");
    else
        printf("\r\n char is DIFF!!!");

    if (c == d)
        printf("\r\n int is SAME!!!");
    else
        printf("\r\n int is DIFF!!!");

    return 0;
}

이 프로그램의 경우 출력이 표시됩니다.

char은 DIFF입니다! int는 동일합니다 !!!

두 가지 모두에 대해 다른 출력을 얻는 이유는 무엇입니까?
출력은 다음과 같아야합니까?

문자는 동일합니다 !!! int는 동일합니다 !!!

codepad 링크 .


이는 C의 다양한 암시 적 유형 변환 규칙 때문입니다. C 프로그래머가 알아야 할 두 가지가 있습니다. 일반적인 산술 변환정수 승격 (후자는 전자의 일부입니다).

char의 경우 유형이 있습니다 (signed char) == (unsigned char). 둘 다 작은 정수 유형 입니다. 다른 작은 정수 유형은 boolshort입니다. 정수 프로모션 규칙 작은 정수형 연산의 피연산자 때마다, 그 유형으로 승진 것이라고 상태 int체결되는을. 이것은 유형이 서명되었는지 여부에 관계없이 발생합니다.

의 경우 signed char기호가 유지되고 int-5 값을 포함하는 로 승격됩니다 . 의 경우 unsigned char251 (0xFB) 값을 포함합니다. int동일한 값을 포함하는 로 승격됩니다 . 당신은

if( (int)-5 == (int)251 )

정수의 경우 유형이 있습니다 (signed int) == (unsigned int). 작은 정수 유형이 아니므로 정수 승격이 적용되지 않습니다. 대신 두 피연산자가 "순위"(크기)는 같지만 부호가 다른 경우 부호있는 피연산자는 부호없는 피연산자와 동일한 유형으로 변환된다는 일반적인 산술 변환 으로 균형을 이룹니다 . 당신은

if( (unsigned int)-5 == (unsigned int)-5)

멋진 질문입니다!

int모두의 int 정확히 같은 비트를 포함하기 때문에 그들은 본질적으로 동일하므로 비교, 작동합니다. 그러나 chars는 어떻습니까?

아, C 는 다양한 경우에 chars를 ints로 암묵적으로 승격 합니다. 이것은 그들 중 하나입니다. 귀하의 코드는이라고 말하지만 if(a==b)컴파일러가 실제로 그것을 바꾸는 것은 다음과 같습니다.

if((int)a==(int)b) 

(int)a-5이지만 (int)b251입니다. 그것들은 확실히 동일하지 않습니다.

편집 : @ Carbonic-Acid가 지적했듯이 (int)ba char가 8 비트 인 경우에만 251 입니다. 경우 int32 비트이며, (int)b-32764이다.

REDIT : 바이트가 8 비트 길이가 아닌 경우 답변의 본질에 대해 설명하는 댓글이 많이 있습니다. 이 경우의 유일한 차이점은 (int)b251 이 아니라 -5가 아닌 다른 양수 라는 것입니다. 이것은 여전히 ​​매우 멋진 질문과는 관련이 없습니다.


정수 프로모션에 오신 것을 환영합니다 . 웹 사이트에서 인용 할 수있는 경우 :

int가 원래 유형의 모든 값을 나타낼 수있는 경우 값은 int로 변환됩니다. 그렇지 않으면 unsigned int로 변환됩니다. 이를 정수 프로모션이라고합니다. 다른 모든 유형은 정수 승격으로 변경되지 않습니다.

이와 같은 비교를 할 때 C는 정말 혼란 스러울 수 있습니다. 최근에 저는 C가 아닌 프로그래밍 친구들 중 일부를 다음과 같은 애타게로 당혹스럽게 만들었습니다.

#include <stdio.h>
#include <string.h>

int main()
{
    char* string = "One looooooooooong string";

    printf("%d\n", strlen(string));

    if (strlen(string) < -1) printf("This cannot be happening :(");

    return 0;
}

실제로 인쇄 This cannot be happening :(하고 25가 -1보다 작다는 것을 보여줍니다!

그러나 아래에서 일어나는 일은 -1이 32 비트 시스템에서 기본 비트 표현으로 인해 4294967295와 같은 부호없는 정수로 표현된다는 것입니다. 그리고 당연히 25는 4294967295보다 작습니다.

그러나에서 size_t반환 된 형식을 strlen부호있는 정수로 명시 적으로 캐스팅하면 :

if ((int)(strlen(string)) < -1)

그런 다음 25와 -1을 비교하고 모든 것이 세계와 잘 어울릴 것입니다.

좋은 컴파일러는 부호없는 정수와 부호있는 정수 간의 비교에 대해 경고해야하지만 여전히 놓치기 쉽습니다 (특히 경고를 활성화하지 않은 경우).

This is especially confusing for Java programmers as all primitive types there are signed. Here's what James Gosling (one of the creators of Java) had to say on the subject:

Gosling: For me as a language designer, which I don't really count myself as these days, what "simple" really ended up meaning was could I expect J. Random Developer to hold the spec in his head. That definition says that, for instance, Java isn't -- and in fact a lot of these languages end up with a lot of corner cases, things that nobody really understands. Quiz any C developer about unsigned, and pretty soon you discover that almost no C developers actually understand what goes on with unsigned, what unsigned arithmetic is. Things like that made C complex. The language part of Java is, I think, pretty simple. The libraries you have to look up.


The hex representation of -5 is:

  • 8-bit, two's complement signed char: 0xfb
  • 32-bit, two's complement signed int: 0xfffffffb

When you convert a signed number to an unsigned number, or vice versa, the compiler does ... precisely nothing. What is there to do? The number is either convertible or it isn't, in which case undefined or implementation-defined behaviour follows (I've not actually checked which) and the most efficient implementation-defined behaviour is to do nothing.

So, the hex representation of (unsigned <type>)-5 is:

  • 8-bit, unsigned char: 0xfb
  • 32-bit, unsigned int: 0xfffffffb

Look familiar? They're bit-for-bit the same as the signed versions.

When you write if (a == b), where a and b are of type char, what the compiler is actually required to read is if ((int)a == (int)b). (This is that "integer promotion" that everyone else is banging on about.)

So, what happens when we convert char to int?

  • 8-bit signed char to 32-bit signed int: 0xfb -> 0xfffffffb
    • Well, that makes sense because it matches the representations of -5 above!
    • It's called a "sign-extend", because it copies the top bit of the byte, the "sign-bit", leftwards into the new, wider value.
  • 8-bit unsigned char to 32-bit signed int: 0xfb -> 0x000000fb
    • This time it does a "zero-extend" because the source type is unsigned, so there is no sign-bit to copy.

So, a == b really does 0xfffffffb == 0x000000fb => no match!

And, c == d really does 0xfffffffb == 0xfffffffb => match!


My point is: didn't you get a warning at compile time "comparing signed and unsigned expression"?

The compiler is trying to inform you that he is entitled to do crazy stuff! :) I would add, crazy stuff will happen using big values, close to the capacity of the primitive type. And

 unsigned int d = -5;

is assigning definitely a big value to d, it's equivalent (even if, probably not guaranteed to be equivalent) to be:

 unsigned int d = UINT_MAX -4; ///Since -1 is UINT_MAX

Edit:

However, it is interesting to notice that only the second comparison gives a warning (check the code). So it means that the compiler applying the conversion rules is confident that there won't be errors in the comparison between unsigned char and char (during comparison they will be converted to a type that can safely represent all its possible values). And he is right on this point. Then, it informs you that this won't be the case for unsigned int and int: during the comparison one of the 2 will be converted to a type that cannot fully represent it.

For completeness, I checked it also for short: the compiler behaves in the same way than for chars, and, as expected, there are no errors at runtime.

.

Related to this topic, I recently asked this question (yet, C++ oriented).

참고URL : https://stackoverflow.com/questions/17312545/type-conversion-unsigned-to-signed-int-char

반응형