c optimization gcc pre-increment gcc4.4

c - Las expresiones "j=++(i | i); y j=++(i & i); ¿deberían ser un error lvalue?



optimization gcc (5)

C11 (n1570), § 6.5.3.1 Operadores de incremento e incremento de prefijo
El operando del operador de incremento o disminución del prefijo debe tener un tipo de puntero o de puntero real, calificado o no calificado, y debe ser un valor l modificable .

C11 (n1570), § 6.3.2.1 Valores L, matrices y designadores de funciones
Un lvalue modificable es un lvalue que no tiene tipo de matriz, no tiene un tipo incompleto, no tiene un tipo const, y si es una estructura o unión, no tiene ningún miembro (incluido, recursivamente, cualquier miembro o elemento de todos los agregados o uniones contenidos) con un tipo de const-calificado.

C11 (n1570), § 6.3.2.1 Valores L, matrices y designadores de funciones
Un lvalue es una expresión (con un tipo de objeto distinto de void ) que designa potencialmente un objeto.

C11 (n1570), § 3. Términos, definiciones y símbolos
Objeto: región de almacenamiento de datos en el entorno de ejecución, cuyos contenidos pueden representar valores

Por lo que yo sé, potencialmente significa "capaz de existir pero aún no existe". Pero (i | i) no es capaz de hacer referencia a una región como almacenamiento de datos en el entorno de ejecución. Por lo tanto, no es un valor l. Esto parece ser un error en una versión antigua de gcc, corregido desde entonces. ¡Actualiza tu compilador!

Esperaba eso en mi siguiente código:

#include<stdio.h> int main(){ int i = 10; int j = 10; j = ++(i | i); printf("%d %d/n", j, i); j = ++(i & i); printf("%d %d/n", j, i); return 1; }

expresiones j = ++(i | i); y j = ++(i & i); producirá errores lvalue de la siguiente manera:

x.c: In function ‘main’: x.c:6: error: lvalue required as increment operand x.c:9: error: lvalue required as increment operand

Pero me sorprendió que el código anterior se haya compilado correctamente, como se muestra a continuación:

~$ gcc x.c -Wall ~$ ./a.out 11 11 12 12

Compruebe que el código anterior funciona correctamente.

Mientras que otros operadores producen errores (según entiendo). Incluso el operador bit a bit XOR causa un error j = ++(i ^ i); (compruebe que otros operadores producen un error lvalue en el momento de la compilación).

¿Cual es la razon? ¿Esto no está especificado o no está definido? u O a nivel de bit O los operadores son diferentes?

versión del compilador:

gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5)

Pero creo que la versión del compilador no debe razonar por un comportamiento no uniforme. Si ^ no compilado, entonces | y también de lo contrario debería funcionar para todos

No es un error con este compilador en el modo c99: gcc xc -Wall -std=c99 .


Este es un error que se ha abordado en versiones más recientes de GCC.

Probablemente sea porque el compilador optimiza i & i para i i | i i | i a i . Esto también explica por qué el operador xor no funcionó; i ^ i estaría optimizado a 0 , que no es un valor l modificable.


No creo que sea un error de optimización, porque si lo fuera, no debería haber ningún error en primer lugar. Si ++(i | i) está optimizado para ++(i) , entonces no debería haber ningún error, porque (i) es un valor l.

En mi humilde opinión, creo que el compilador ve (i | i) como un resultado de expresión, que, obviamente, genera rvalue, pero el operador de incremento ++ espera que un lvalue lo modifique, y por lo tanto el error.


Solo una continuación de mi pregunta. Agregué una respuesta elaborada para que pueda ser útil.

En mis expresiones de código j = ++(i | i); y j = ++(i & i); no son causados ​​por el error lvalue?

Debido a la optimización del compilador como @abelenky respondió (i | i) == i (i & i) == i . Eso es exactamente CORRECTO.

En mi compilador (gcc version 4.4.5) , cualquier expresión que incluya variable única y resultado no se modifica; optimizado en una sola variable (algo llamado no una expresión ).

por ejemplo:

j = i | i ==> j = i j = i & i ==> j = i j = i * 1 ==> j = i j = i - i + i ==> j = i

==> significa optimized to

Para observarlo escribí un pequeño código C y lo desensamblé con gcc -S .

C-Code: ( leer comentarios )

#include<stdio.h> int main(){ int i = 10; int j = 10; j = i | i; //==> j = i printf("%d %d", j, i); j = i & i; //==> j = i printf("%d %d", j, i); j = i * 1; //==> j = i printf("%d %d", j, i); j = i - i + i; //==> j = i printf("%d %d", j, i); }

resultado de ensamblaje: ( leer comentarios )

main: pushl %ebp movl %esp, %ebp andl $-16, %esp subl $32, %esp movl $10, 28(%esp) // i movl $10, 24(%esp) // j movl 28(%esp), %eax //j = i movl %eax, 24(%esp) movl $.LC0, %eax movl 28(%esp), %edx movl %edx, 8(%esp) movl 24(%esp), %edx movl %edx, 4(%esp) movl %eax, (%esp) call printf movl 28(%esp), %eax //j = i movl %eax, 24(%esp) movl $.LC0, %eax movl 28(%esp), %edx movl %edx, 8(%esp) movl 24(%esp), %edx movl %edx, 4(%esp) movl %eax, (%esp) call printf movl 28(%esp), %eax //j = i movl %eax, 24(%esp) movl $.LC0, %eax movl 28(%esp), %edx movl %edx, 8(%esp) movl 24(%esp), %edx movl %edx, 4(%esp) movl %eax, (%esp) call printf movl 28(%esp), %eax //j = i movl %eax, 24(%esp) movl $.LC0, %eax movl 28(%esp), %edx movl %edx, 8(%esp) movl 24(%esp), %edx movl %edx, 4(%esp) movl %eax, (%esp) call printf

En el código de ensamblaje anterior, todas las expresiones se convierten en el siguiente código:

movl 28(%esp), %eax movl %eax, 24(%esp)

eso es equivalente a j = i en el código C. Por lo tanto, j = ++(i | i); j = ++(i & i); están optimizados para j = ++i .

Aviso: j = (i | i) es una declaración donde como expresión (i | i) no es una declaración (nop) en C

Por lo tanto, mi código podría compilarse con éxito.

Por qué j = ++(i ^ i); o j = ++(i * i); , j = ++(i | k); ¿produce un error de lvalue en mi compilador?

Porque cualquiera de las expresiones tiene el valor constante o lvalue no modificable (expresión no optimizada).

podemos observar usando el código asm

#include<stdio.h> int main(){ int i = 10; int j = 10; j = i ^ i; printf("%d %d/n", j, i); j = i - i; printf("%d %d/n", j, i); j = i * i; printf("%d %d/n", j, i); j = i + i; printf("%d %d/n", j, i); return 1; }

código de ensamblaje: ( leer comentarios )

main: pushl %ebp movl %esp, %ebp andl $-16, %esp subl $32, %esp movl $10, 28(%esp) // i movl $10, 24(%esp) // j movl $0, 24(%esp) // j = i ^ i; // optimized expression i^i = 0 movl $.LC0, %eax movl 28(%esp), %edx movl %edx, 8(%esp) movl 24(%esp), %edx movl %edx, 4(%esp) movl %eax, (%esp) call printf movl $0, 24(%esp) //j = i - i; // optimized expression i - i = 0 movl $.LC0, %eax movl 28(%esp), %edx movl %edx, 8(%esp) movl 24(%esp), %edx movl %edx, 4(%esp) movl %eax, (%esp) call printf movl 28(%esp), %eax //j = i * i; imull 28(%esp), %eax movl %eax, 24(%esp) movl $.LC0, %eax movl 28(%esp), %edx movl %edx, 8(%esp) movl 24(%esp), %edx movl %edx, 4(%esp) movl %eax, (%esp) call printf movl 28(%esp), %eax // j = i + i; addl %eax, %eax movl %eax, 24(%esp) movl $.LC0, %eax movl 28(%esp), %edx movl %edx, 8(%esp) movl 24(%esp), %edx movl %edx, 4(%esp) movl %eax, (%esp) call printf movl $1, %eax leave

Por lo tanto, esto produce un lvalue error porque el operando no es un valor l modificable. Y el comportamiento no uniforme se debe a la optimización del compilador en gcc-4.4.

¿Por qué los nuevos cumplidores de gcc (o la mayoría de los compiladores) producen un error de lvalue?

Porque la evaluación de la expresión ++(i | i) y ++(i & i) prohíbe la definación real del operador de incremento (++).

Según el libro de Dennis M. Ritchie " The C Programming Language " en la sección "2.8 Increment and Decrement Operators" página 44.

Los operadores de incremento y decremento solo se pueden aplicar a las variables; una expresión como (i + j) ++ es ilegal. El operando debe ser un valor l modificable de tipo aritmético o de puntero.

Probé en el nuevo compilador de gcc 4.47 aquí produce un error como esperaba. También probé en el compilador tcc.

Cualquier comentario / comentario sobre esto sería genial.


Tiene razón en que no debería compilarse, y en la mayoría de los compiladores, no compila.
(Especifique exactamente qué compilador / versión NO le está dando un error de compilación)

Solo puedo hipotetizar que el compilador conoce las identidades que (i | i) == i (i & i) == i y está usando esas identidades para optimizar la expresión, simplemente dejando atrás la variable i .

Esto es solo una suposición, pero tiene mucho sentido para mí.