Tengo un código como el siguiente bloque de código (no puedo publicar el código original) dentro de un archivo .cpp
que creo que está siendo compilado por clang++
(Ubuntu clang version 3.5.2-3ubuntu1 (tags/RELEASE_352/final) (based on LLVM 3.5.2)
).
Parece el código C
, porque estamos usando GoogleTest
para probar nuestro código C
. De todos modos:
size_t const SHIFT = 4;
uint8_t var, var2;
/* Omitted: Code that sets var to, say 00011000 (base 2) */
var2 = var;
var = var << SHIFT >> SHIFT; // [1] result is 00011000 (base 2) (tested with printf)
var2 = var2 << SHIFT;
var2 = var2 >> SHIFT; // [2] result is 00001000 (base 2) (tested with printf)
Ahora, ¿por qué el comentario [1]
es cierto? Supuse que la línea correspondiente daría como resultado que los 4 bits superiores se pusieran a cero. Pero descubrí que esto no es cierto; el programa simplemente restaura el valor original.
¿Es este un comportamiento definido por el lenguaje, o está
clang
compilando un cambio de bits supuestamente inútil?
(Verifiqué la asociatividad (usando esta tabla en cppreference.com, asumiendo que la asociatividad / La precedencia de los operadores básicos no diferirá entre las versiones de C++
y probablemente tampoco entre C++
y C
, al menos no en las 'versiones actuales') y parece que la expresión RHS en [1]
realmente debería producir el mismo resultado que las dos siguientes declaraciones)
2 respuestas
Lo que está viendo es el resultado de promociones de números enteros . Cualquier valor con un tipo de rango inferior a int
, cuando se usa en una expresión, se promueve a int
.
Esto se detalla en la sección 6.3.1.1 del estándar C
2 Lo siguiente puede usarse en una expresión siempre que un
int
ounsigned int
pueda ser usado:
- Un objeto o expresión con un tipo de número entero (que no sea
int
ounsigned int
) cuya clasificación de conversión de números enteros es menor o igual que la clasificación deint
yunsigned int
.- Un campo de bits de tipo
_Bool
,int
,signed int
ounsigned int
.Si un
int
puede representar todos los valores del tipo original (según lo restringido por el ancho, para un campo de bits), el valor se convierte en unint
; de lo contrario, se convierte enunsigned int
. Se denominan promociones de números enteros . Todos los demás tipos no se modifican por el promociones enteras.
En este caso:
var = var << SHIFT >> SHIFT;
var
primero asciende a int
. Este tipo tiene al menos 16 bits de ancho y probablemente 32 bits de ancho. Entonces, el valor con el que se opera es 0x00000018
. Un desplazamiento a la izquierda de 4 da como resultado 0x00000180
y, posteriormente, un desplazamiento a la derecha da como resultado 0x00000018
.
El resultado luego se almacena en un uint_8
. Dado que el valor encaja en una variable de este tipo, no se requiere conversión y se almacena 0x18
.
La expresion:
var = var << SHIFT >> SHIFT;
No es semánticamente equivalente a
var = var << SHIFT ;
var = var >> SHIFT ;
Eso requeriría:
var = (uint8_t)(var << SHIFT) >> SHIFT ;
Lo que ilustra la diferencia efectiva entre los dos: el comportamiento observado no requiere optimización, sino que es requerido por la definición del lenguaje con respecto a las reglas de promoción de tipos en expresiones.
Dicho esto, también es muy posible que un compilador pueda optimizar los cambios. La optimización no puede cambiar el resultado cuando el comportamiento está definido como en este caso.
Preguntas relacionadas
Nuevas preguntas
c++
C ++ es un lenguaje de programación de propósito general. Originalmente fue diseñado como una extensión de C y tiene una sintaxis similar, pero ahora es un lenguaje completamente diferente. Utilice esta etiqueta para preguntas sobre el código (a ser) compilado con un compilador C ++. Utilice una etiqueta de versión específica para preguntas relacionadas con una revisión estándar específica [C ++ 11], [C ++ 14], [C ++ 17], [C ++ 20] o [C ++ 23], etc. .