44 votes

Les expressions "j = ++(i | i) ; et j = ++(i & i) ; devraient être une erreur de valeur l ?

Je m'attendais à cela dans mon code suivant :

#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;
}

expressions j = ++(i | i); y j = ++(i & i); produira des erreurs de type lvalue comme ci-dessous :

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

Mais j'ai été surpris de constater que le code ci-dessus a été compilé avec succès, comme ci-dessous :

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

Consulte le code ci-dessus fonctionne correctement.

Alors que d'autres opérateurs produisent des erreurs (d'après ce que j'ai compris). Même l'opérateur binaire XOR provoque une erreur. j = ++(i ^ i); (cochez d'autres Les opérateurs produisent une erreur de type lvalue au moment de la compilation).

Quelle en est la raison ? Est-ce que c'est non spécifié ou non défini ? ou les opérateurs OR et AND sont différents ?

la version du compilateur :

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

Mais je pense que la version du compilateur ne devrait pas être la raison d'un comportement non uniforme. Si <code>^</code> pas compilé alors <code>|</code> y <code>&</code> pas non plus. sinon, cela devrait fonctionner pour tous

Ce n'est pas une erreur avec ce compilateur en mode c99 : gcc x.c -Wall -std=c99 .

28voto

abelenky Points 28063

Vous avez raison de dire qu'il ne devrait pas compiler, et sur la plupart des compilateurs, il ne compile pas.
(Veuillez spécifier exactement quel compilateur/version ne vous donne PAS d'erreur de compilation)

Je ne peux qu'émettre l'hypothèse que le compilateur connaît les identités que (i | i) == i y (i & i) == i et utilise ces identités pour optimiser l'expression, en laissant derrière elle la variable i .

Ce n'est qu'une supposition, mais ça me paraît très logique.

25voto

antonijn Points 2998

Il s'agit d'un bogue qui a été corrigé dans les versions plus récentes de GCC.

C'est probablement parce que le compilateur optimise i & i a i y i | i a i . Cela explique aussi pourquoi l'opérateur xor ne fonctionne pas ; i ^ i serait optimisé pour 0 qui n'est pas une valeur l modifiable.

17voto

md5 Points 14957

C11 (n1570), § 6.5.3.1 Opérateurs d'incrémentation et de décrémentation de préfixes
L'opérande de l'opérateur d'incrémentation ou de décrémentation préfixe doit être atomique, qualifié, ou non qualifié, et doit être de type réel ou pointeur. valeur l modifiable .

C11 (n1570), § 6.3.2.1 Valeurs L, tableaux et désignateurs de fonctions
Une lvalue modifiable est une lvalue qui n'a pas de type tableau, n'a pas de type incomplet, n'a pas de type qualifié de const. et, s'il s'agit d'une structure ou d'une union, n'a pas de membre (y compris, récursivement, tout membre ou élément de tous les agrégats ou unions contenus) avec un type qualifié const-. qualifié.

C11 (n1570), § 6.3.2.1 Valeurs L, tableaux et désignateurs de fonctions
Une lvalue est une expression (avec un type d'objet autre que void ) qui potentiellement désigne un objet.

C11 (n1570), § 3. Termes, définitions et symboles
Objet : Région de stockage de données dans l'environnement d'exécution, dont le contenu peut représenter des valeurs

Pour autant que je sache, potentiellement signifie "capable d'être mais pas encore existant". Mais (i | i) n'est pas capable de référencer une région ou un stockage de données dans l'environnement d'exécution. Ce n'est donc pas une lvalue. Cela semble être un bug dans une ancienne version de gcc, corrigé depuis. Mettez à jour votre compilateur !

7voto

Grijesh Chauhan Points 28442

Juste un suivi de ma question . J'ai ajouté une réponse élaborée pour que quelqu'un puisse la trouver utile.

Dans mes expressions de code j = ++(i | i); et j = ++(i & i); ne sont pas causés par une erreur de valeur lvalue ?

A cause de l'optimisation du compilateur comme l'a répondu @abelenky (i | i) == i y (i & i) == i . C'est exactement CORRECT.

Dans mon compilateur (gcc version 4.4.5) toute expression comprenant une seule variable et dont le résultat est inchangé ; optimisée en une seule variable (ce que l'on nomme pas une expression ).

par exemple :

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

<code>==></code> signifie <code>optimized to</code>

Pour l'observer, j'ai écrit un petit code C et je l'ai désassemblé avec gcc -S .

C-Code : ( lire les commentaires )

#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);
}

sortie de l'assemblage : ( lire les commentaires )

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  

Dans le code d'assemblage ci-dessus, toutes les expressions sont converties en code suivant :

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

qui est équivalent à j = i en code C. Ainsi, j = ++(i | i); et et j = ++(i & i); sont optimisés pour j = ++i .

Avis : j = (i | i) est une déclaration alors que l'expression (i | i) pas une déclaration (nop) en C

Ainsi, mon code a pu être compilé avec succès.

*Pourquoi j = ++(i ^ i); o `j = ++(i i);,j = ++(i | k);` produit une erreur lvalue sur mon compilateur ?**

Parce que l'une ou l'autre expression a la valeur constante ou lvalue non modifiable (expression non optimisée).

nous pouvons observer en utilisant <code>asm</code> code

#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;
}

code d'assemblage : ( lire les commentaires )

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

Ainsi, cela produit un lvalue error parce que l'opérande n'est pas une valeur l modifiable. Et Le comportement non-uniforme est dû à l'optimisation du compilateur dans gcc-4.4.

Pourquoi les nouveaux compilateurs gcc (ou la plupart des compilateurs) produisent une erreur lvalue ?

Parce que l'évaluation de l'expression ++(i | i) y ++(i & i) interdit la définition réelle de l'opérateur d'incrémentation (++).

Selon le livre de Dennis M. Ritchie " Le langage de programmation C " dans la section "2.8 Opérateurs d'incrémentation et de décrémentation" page 44.

Les opérateurs d'incrémentation et de décrémentation ne peuvent être appliqués qu'à des variables ; une expression comme (i+j)++ est illégale. L'opérande doit être une lvalue modifiable de type arithmétique ou pointeur.

J'ai testé sur de nouvelles compilateur gcc 4.47 ici il produit l'erreur à laquelle je m'attendais. J'ai également testé sur le compilateur tcc.

Toute réaction ou commentaire à ce sujet serait le bienvenu.

1voto

Ghasan Points 553

Je ne pense pas du tout que ce soit une erreur d'optimisation, car si c'était le cas, il ne devrait pas y avoir d'erreur en premier lieu. Si ++(i | i) est optimisé pour ++(i) alors il ne devrait pas y avoir d'erreur, car (i) est une lvalue.

IMHO, je pense que le compilateur voit (i | i) comme une sortie d'expression, qui, évidemment, produit rvalue, mais l'opérateur d'incrémentation ++ attend une lvalue pour le modifier, d'où l'erreur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X