sanketssj5
Estaba leyendo un capítulo sobre operadores bit a bit, me encontré con el programa de operador de complemento de 1 y decidí ejecutarlo en Visual C++.
int main ()
{
unsigned char c = 4, d;
d = ~c;
printf("%d\n", d);
}
Da la salida válida: 251
Entonces en vez de usar d
como una variable para mantener el valor de ~c
decidí imprimir directamente el valor de ~c
.
int main ()
{
unsigned char c=4;
printf("%d\n", ~c);
}
Da la salida -5
.
¿Por qué no funcionó?
Grzegorz Szpetkowski
En esta declaración:
printf("%d",~c);
la c
se convierte en int
1 escribe antes de ~
(complemento bit a bit) se aplica el operador. Esto se debe a promociones enterasque se invocan al operando del ~
. En este caso un objeto de unsigned char
el tipo se promociona a (firmado) int
que es entonces (después de ~
evaluación del operador) utilizado por printf
función, con correspondencia %d
especificador de formato.
Darse cuenta de promociones de argumento predeterminado (como printf
es una función variádica) no juega ningún papel aquí, ya que el objeto ya es del tipo int
.
Por otro lado, en este código:
unsigned char c = 4, d;
d = ~c;
printf("%d", d);
ocurren los siguientes pasos:
c
es un sujeto de promociones enteras porque~
(de la misma manera, como se describe arriba)~c
rvalue se evalúa como (firmado)int
valor (por ejemplo-5
)d=~c
hace una conversión implícita deint
aunsigned char
comod
tiene ese tipo. Puedes pensar que es lo mismo qued = (unsigned char) ~c
. Darse cuenta ded
no puede ser negativo (esta es la regla general para todos los tipos sin firmar).printf("%d", d);
invoca promociones de argumento predeterminadode este modod
se convierte enint
y el valor (no negativo) se conserva (es decir, elint
type puede representar todos los valores deunsigned char
escribe).
1) suponiendo que int
puede representar todos los valores de unsigned char
(ver el comentario de TC a continuación), pero es muy probable que suceda de esta manera. Más específicamente, suponemos que INT_MAX >= UCHAR_MAX
sostiene Típicamente el sizeof(int) > sizeof(unsigned char)
las retenciones y el byte constan de ocho bits. De lo contrario, el c
se convertiría en unsigned int
(según la subcláusula C11 §6.3.1.1/p2), y el especificador de formato también debe cambiarse en consecuencia a %u
para evitar obtener un UB (C11 §7.21.6.1/p9).
-
Pero luego esto también sucede en
d=~c
. La diferencia real entonces es la conversión (regreso) aunsigend char
eso pasa end=~c
pero no en elprintf
llamar.–Marc van Leeuwen
17 de febrero de 2015 a las 12:44
-
@MarcvanLeeuwen: El
d
tiene tipounsigned char
por lo que no puede ser negativo (incluso después de la conversión aint
porqueprintf
‘s promociones de argumento predeterminado). De hecho, tienes razón en que end=~c
la promoción de enteros también ha tenido lugar (de la misma manera, como describí en la respuesta anterior), pero la asignación convierteint
aunsigned char
de nuevo. Por otra parte, en el segundo caso,printf
la función tomaint
argumento “tal cual” (es decir,%d
especificador de formato es correcto), por lo que promociones de argumentos predeterminados no hacer nada allí.-Grzegorz Szpetkowski
17 de febrero de 2015 a las 12:56
-
Para más lectores: actualicé mi respuesta para reflejar la observación de Marc. Puede encontrar la respuesta original (más corta) en el historial de revisiones. Espero que todo esté claro ahora.
-Grzegorz Szpetkowski
17/02/2015 a las 19:36
-
Tenga en cuenta que en algunos sistemas
unsigned char
podría ser promovido aunsigned int
por las promociones enteras (por ejemplo, sisizeof(int) == 1
), en cuyo caso elprintf
invocaría un comportamiento indefinido porque utilizó el especificador incorrecto.– CT
18 de febrero de 2015 a las 15:08
-
@TC: Tiene razón, pero supongamos (para simplificar) que vivimos en un mundo donde un byte consta de ocho bits. Sé que el Estándar no lo hace cumplir, pero, por ejemplo, POSIX sí lo hace.
-Grzegorz Szpetkowski
18 de febrero de 2015 a las 15:18
trucos
char
es ascendido a int
en printf
declaración antes de la operación ~
en segundo fragmento. Asi que c
cual es
0000 0100 (2's complement)
en binario se promociona a (suponiendo una máquina de 32 bits)
0000 0000 0000 0000 0000 0000 0000 0100 // Say it is x
y su complemento bit a bit es igual al complemento a dos del valor menos uno (~x = −x − 1
)
1111 1111 1111 1111 1111 1111 1111 1011
cual es -5
en decimal en forma de complemento a 2.
Tenga en cuenta que la promoción predeterminada de char
c
a int
también se realiza en
d = ~c;
antes de la operación de complemento, pero el resultado se convierte de nuevo a unsigned char
como d
es de tipo unsigned char
.
C11: 6.5.16.1 Asignación simple (p2):
En asignación simple (
=
), el valor del operando derecho se convierte al tipo de la expresión de asignación y reemplaza el valor almacenado en el objeto designado por el operando izquierdo.
y
6.5.16 (p3):
El tipo de una expresión de asignación es el tipo que tendría el operando izquierdo después de la conversión de lvalue.
grijesh chauhan
Para entender el comportamiento de su código, necesita aprender el concepto llamado ‘Promociones enteras’ (eso sucede en su código implícitamente antes de la operación bit a bit NO en un unsigned char
operando) Como se menciona en el borrador del comité N1570:
§ 6.5.3.3 Operadores aritméticos unarios
- el resultado de la
~
operador es el complemento bit a bit de su operando (promovido) (es decir, cada bit en el resultado se establece si y solo si el bit correspondiente en el operando convertido no está establecido). Las promociones enteras se realizan en el operando, y el resultado tiene el tipo promovido. Si el tipo promocionado es un ” ‘tipo sin firmar’, la expresión~E
es equivalente al valor máximo representable en ese tipo menosE
“.
Porque unsigned char
el tipo es más estrecho que (ya que requiere menos bytes) int
tipo, – promoción de tipo implícita realizada por máquina abstracta (compilador) y valor de variable c
es ascendido a int
en el momento de la compilación (antes de la aplicación de la operación complemento ~
). Es necesario para la correcta ejecución del programa porque ~
necesita un operando entero.
§ 6.5 Expresiones
- Algunos operadores (el operador unario
~
, y los operadores binarios<<
,>>
,&
,^
y|
descritos colectivamente como operadores bit a bit) se requiere que tengan operandos de tipo entero. Estos operadores generan valores que dependen de las representaciones internas de los enteros y tienen aspectos definidos por la implementación y no definidos para los tipos con signo.
Los compiladores son lo suficientemente inteligentes como para analizar expresiones, verificar la semántica de las expresiones, realizar verificación de tipos y conversiones aritméticas si es necesario. Esa es la razón por la que para aplicar ~
en char
type no necesitamos escribir explícitamente ~(int)c
– llamado conversión de tipo explícito (y evita errores).
Nota:
-
Valor de
c
es ascendido aint
en expresión~c
pero tipo dec
es todavíaunsigned char
– su tipo no lo hace. No se confunda. -
Importante: consecuencia de
~
la operación es deint
escriba!, verifique el código a continuación (no tengo vs-compiler, estoy usando 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; }
Compílalo y ejecuta:
$ gcc -std=gnu99 -Wall -pedantic x.c -o x $ ./x sizeof(int) = 4, sizeof(unsigned char) = 1 sizeof(~c) = 4
Aviso: tamaño del resultado de
~c
es igual que deint
pero no es igual aunsigned char
– consecuencia de~
operador en esta expresión esint
! que como se mencionó 6.5.3.3 Operadores aritméticos unarios- El resultado de la unaria
-
operador es el negativo de su operando (promovido). Las promociones enteras se realizan en el operando, y el resultado tiene el tipo promocionado.
- El resultado de la unaria
Ahora, como @haccks también explicó en su respuesta, ese resultado de ~c
en una máquina de 32 bits y por valor de c = 4
es:
1111 1111 1111 1111 1111 1111 1111 1011
en decimal es -5
– esa es la salida de su segundo código!
En tus primer códigouna línea más es interesante de entender b = ~c;
porque b
es un unsigned char
variable y resultado de ~c
es de int
tipo, para acomodar el valor del resultado de ~c
a b
el valor del resultado (~c) es truncado para encajar en el tipo de carácter sin firmar como sigue:
1111 1111 1111 1111 1111 1111 1111 1011 // -5 & 0xFF
& 0000 0000 0000 0000 0000 0000 1111 1111 // - one byte
-------------------------------------------
1111 1011
Equivalente decimal de 1111 1011
es 251
. Podrías obtener el mismo efecto usando:
printf("\n ~c = %d", ~c & 0xFF);
o como lo sugiere @ouah en su respuesta usando casting explícito.
-
@haccks gracias, sí, principalmente vengo aquí para aprender más que para participar y actualmente estoy trabajando principalmente en python, django, productos web.
–Grijesh Chauhan
17 de febrero de 2015 a las 17:54
-
+int(PI/3) por citar realmente las partes de especificaciones relevantes… ninguna otra respuesta explica realmente por qué la promoción pasa aquí! esto->
Some operators (the unary operator ~, and the binary operators <<, >>, &, ^, and |, collectively described as bitwise operators) are required to have operands that have integer type.
hizo mi día hoy.– usuario719662
18 de febrero de 2015 a las 9:25
-
@vaxquis gracias, cuando llegué a esta publicación me di cuenta de que la mayoría de las respuestas explicaban “en qué se diferencian los resultados”, así que decidí agregar mi respuesta y enfatizar la razón de “por qué”, en el momento en que leíste mi respuesta me faltaba uno más relevante del borrador, ahora agregado.
–Grijesh Chauhan
18 de febrero de 2015 a las 15:09
Al aplicar el ~
operador a c
se promociona a int
el resultado es un int
también.
Después
- en el primer ejemplo, el resultado se convierte en
unsigned char
y luego promovido asigned int
e impreso. - en el segundo ejemplo, el resultado se imprime como
signed int
.
Da la op -5. ¿Por qué no funcionó?
En vez de:
printf("%d",~c);
usar:
printf("%d", (unsigned char) ~c);
para obtener el mismo resultado que en su primer ejemplo.
~
El operando se somete a la promoción de enteros y la promoción de argumentos predeterminada se aplica al argumento de las funciones variádicas.
david ranieri
Promoción de enteros, del estándar:
Si el tipo del operando con tipo entero con signo puede representar todos los valores del tipo del operando con tipo entero sin signo, el operando con tipo entero sin signo se convertirá al tipo del operando con tipo entero con signo.
Pista: Complemento a 1 de
4
es equivalente a la representación en complemento a dos de-5
– Vago
17 de febrero de 2015 a las 10:55
es complementarno cumplido
– phuclv
18 de febrero de 2015 a las 15:46
@LưuVĩnhPhúc tu complemento merece un cumplido.
– Deja Vu
02/03/2015 a las 22:57