IT박스

보수가 printf를 통해 다르게 작동하는 이유는 무엇입니까?

itboxs 2020. 12. 15. 08:28
반응형

보수가 printf를 통해 다르게 작동하는 이유는 무엇입니까?


비트 연산자에 대한 장을 읽고 있었고 1의 보완 연산자 프로그램을 발견하고 Visual C ++에서 실행하기로 결정했습니다.

int main ()
{
   unsigned char c = 4, d;
   d = ~c;
   printf("%d\n", d);
}

유효한 출력을 제공합니다. 251

그런 다음 d의 값을 유지하기 위해 변수로 사용 하는 대신의 ~c을 직접 인쇄하기로 결정했습니다 ~c.

int main ()
{
   unsigned char c=4;
   printf("%d\n", ~c);
}

그것은 출력을 제공합니다 -5.

왜 작동하지 않았습니까?


이 진술에서 :

printf("%d",~c);

c변환되는 int1 ~ (비트 보수) 연산자가인가된다. 이는의 피연산자에 대해 호출되는 정수 프로모션 때문입니다 ~. 이 경우 unsigned char유형 의 객체는 (signed)로 승격되며 int, 이는 일치하는 형식 지정자 와 함께 함수에서 ~사용되는 ( 연산자 평가 ) 사용됩니다 .printf%d

객체가 이미 유형 이므로 기본 인수 승격 ( printf가변 함수 와 마찬가지로 )은 여기서 어떤 역할도하지 않습니다 int.

반면에이 코드에서 :

unsigned char c = 4, d;
d = ~c;
printf("%d", d);

다음 단계가 발생합니다.

  • c되는 피사체 정수 프로모션 때문에 ~(동일한 방식으로 전술 한 바와 같이)
  • ~c를 rvalue는 다음과 같이 평가 (서명) int값 (예를 들어 -5)
  • d=~c에서 암시 적 변환하게 int행을 unsigned char같이, d이러한 유형이있다. 당신은 그것을 d = (unsigned char) ~c. d음수 될 수 없습니다 (모든 서명되지 않은 유형에 대한 일반 규칙).
  • printf("%d", d);기본 인수 promotions을 호출 하므로 d로 변환되고 int(네거티브가 아닌) 값이 보존됩니다 (즉, int유형은 유형의 모든 값을 나타낼 수 있음 unsigned char).

1) int모든 값을 나타낼 수 있다고 가정 합니다 unsigned char(아래 TC의 의견 참조).하지만 이런 식으로 발생할 가능성 매우 높습니다. 더 구체적으로, 우리는 그것이 INT_MAX >= UCHAR_MAX성립 한다고 가정합니다 . 일반적으로 sizeof(int) > sizeof(unsigned char)홀드와 바이트는 8 비트로 구성됩니다. 그렇지 않으면는 (C11 하위 절 §6.3.1.1 / p2에서와 같이)로 c변환되고 UB (C11 §7.21.6.1 / p9)를 얻지 않도록 unsigned int그에 따라 형식 지정자도 변경해야합니다 %u.


char두 번째 스 니펫 의 작업 전에 intin printf으로 승격됩니다 ~. 그래서 c,

0000 0100 (2's complement)  

바이너리로 승격됩니다 (32 비트 시스템 가정).

0000 0000 0000 0000 0000 0000 0000 0100 // Say it is x  

비트 단위 보수는 값에서 1을 뺀 값의 2의 보수와 같습니다 ( ~x = −x − 1)

1111 1111 1111 1111 1111 1111 1111 1011  

이는 -52의 보수 형태의 진수.

의 기본 추진 것을 참고 char c로이 int또한 수행한다

d = ~c;

보수 작업 전에 결과는 형식 unsigned char그대로 다시 변환됩니다 .dunsigned char

C11 : 6.5.16.1 단순 할당 (p2) :

단순 대입 ( =)에서는 오른쪽 피연산자의 값이 대입 식의 유형으로 변환되고 왼쪽 피연산자가 지정한 오브젝트에 저장된 값을 대체합니다.

6.5.16 (p3) :

할당 표현식의 유형은 lvalue 변환 후 왼쪽 피연산자가 갖는 유형입니다.


코드의 동작을 이해하려면 N1570위원회 초안에 언급 된대로 'Integer Promotions' 라는 개념을 배워야합니다 ( unsigned char피연산자 에 대한 비트 현명한 NOT 연산 전에 암시 적으로 코드에서 발생 함 ).

§ 6.5.3.3 단항 산술 연산자

  1. ~연산자 의 결과는 (승격 된) 피연산자의 비트 단위 보수입니다 (즉, 변환 된 피연산자의 해당 비트가 설정되지 않은 경우에만 결과의 각 비트가 설정 됨). 정수 승격은 피연산자에서 수행되며 결과는 승격 된 유형 입니다. 승격 된 유형이 " '부호없는 유형'인 경우 표현식 ~E은 해당 유형에서 나타낼 수있는 최대 값에서 E"을 뺀 값과 동일합니다 .

Because unsigned char type is narrower than (as it requires fewer bytes) int type, - implicit type promotion performed by abstract machine(compiler) and value of variable c is promoted to int at the time of compilation (before application of the complement operation ~). It is required for the correct execution of the program because ~ need an integer operand.

§ 6.5 Expressions

  1. Some operators (the unary operator ~, and the binary operators <<, >>, &, ^, and |, collectively described as bitwise operators) are required to have operands that have integer type. These operators yield values that depend on the internal representations of integers, and have implementation-defined and undefined aspects for signed types.

Compilers are smart-enough to analyze expressions, checks semantics of expressions, perform type checking and arithmetic conversions if required. That's the reason that to apply ~ on char type we don't need to explicitly write ~(int)c — called explicit type casting (and do avoid errors).

Note:

  1. Value of c is promoted to int in expression ~c, but type of c is still unsigned char - its type does not. Don't be confused.

  2. Important: result of ~ operation is of int type!, check below code (I don't have vs-compiler, I am using gcc):

    #include<stdio.h>
    #include<stdlib.h>
    int main(void){
       unsigned char c = 4;
       printf(" sizeof(int) = %zu,\n sizeof(unsigned char) = %zu",
                sizeof(int),
                sizeof(unsigned char));
       printf("\n sizeof(~c) = %zu", sizeof(~c));        
       printf("\n");
       return EXIT_SUCCESS;
    }
    

    compile it, and run:

    $ gcc -std=gnu99 -Wall -pedantic x.c -o x
    $ ./x
    sizeof(int) = 4,
    sizeof(unsigned char) = 1
    sizeof(~c) = 4
    

    Notice: size of result of ~c is same as of int, but not equals to unsigned char — result of ~ operator in this expression is int! that as mentioned 6.5.3.3 Unary arithmetic operators

    1. The result of the unary - operator is the negative of its (promoted) operand. The integer promotions are performed on the operand, and the result has the promoted type.

Now, as @haccks also explained in his answer -that result of ~c on 32-bit machine and for value of c = 4 is:

1111 1111 1111 1111 1111 1111 1111 1011

in decimal it is -5 — that is the output of your second code!

In your first code, one more line is interesting to understand b = ~c;, because b is an unsigned char variable and result of ~c is of int type, so to accommodate value of result of ~c to b result value (~c) is truncated to fit into the unsigned char type as follows:

    1111 1111 1111 1111 1111 1111 1111 1011  // -5 & 0xFF
 &  0000 0000 0000 0000 0000 0000 1111 1111  // - one byte      
    -------------------------------------------          
                                  1111 1011  

Decimal equivalent of 1111 1011 is 251. You could get same effect using:

printf("\n ~c = %d", ~c  & 0xFF); 

or as suggested by @ouah in his answer using explicitly casting.


When applying the ~ operator to c it gets promoted to int, the result is an int as well.

Then

  • in the 1st example the result gets converted to unsigned char and then promoted to signed int and printed.
  • in the 2nd example the result gets printed as signed int.

It gives the op -5. why it didn't work?

Instead of:

printf("%d",~c);

use:

printf("%d", (unsigned char) ~c);

to get the same result as in your first example.

~ operand undergoes integer promotion and default argument promotion are applied to argument of variadic functions.


Integer promotion, from the standard:

If the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, the operand with unsigned integer type shall be converted to the type of the operand with signed integer type.

ReferenceURL : https://stackoverflow.com/questions/28560245/why-does-the-complement-behave-differently-through-printf

반응형