2010-10-04 7 views
3

Je viens de trouver un bogue qui, bizarrement, ne s'est produit que lorsque l'optimisation a été activée (g++ -O2). Ce fut un Arithmetic exception dans le code suivant, lorsque interval a été mis à zéro (à partir d'un argument de ligne de commande):Pourquoi cette erreur de division par zéro se produit-elle uniquement dans le code optimisé?

for(int i = 0; i < n; ++i) { 
    if((i + 1) % interval == 0) { // exception here 
    DoSomething(); 
    } 
} 

Il est évident que modulo zéro opération a lancé une exception division par zéro, mais pourquoi est-ce que se produire lorsque le code a été compilé avec l'optimisation activée?

+0

Je vous suggère de regarder la sortie de l'assemblage et de voir s'ils effectuent tous la division. Aussi, quel compilateur? g ++? – Kizaru

+1

La chose étrange n'est pas que la version optimisée lève une exception, c'est que la version non-optimisée * n'est pas *. Que fait-il à la place, appelle le résultat '0'? Ou '! 0'? – egrunin

+1

Souciez-vous de partager le programme complet, la version du compilateur et les commutateurs du compilateur? Je ne peux pas reproduire le comportement que vous voyez sur g ++ 4.0.1/OSX. Je reçois une "exception de virgule flottante" à chaque fois sur les versions optimisées et non optimisées. –

Répondre

13

Diviser par zéro est toujours un comportement indéfini. Le fait que vous obteniez des résultats différents avec des paramètres d'optimisation différents correspond toujours à la définition de comportement indéfini.

+0

D'accord, mais j'aimerais en savoir plus. Pourquoi la version optimisée a-t-elle jeté une exception, et la version non optimisée ne semblait pas du tout s'en soucier? – Frank

+0

Essayez ceci est GCC, essayez de compiler avec l'option '-S', pour obtenir la sortie du langage assembleur, puis comparez les différences. – SingleNegationElimination

+0

La spécification de langage n'est pas l'autorité finale sur ce que fait la CPU. Il est juste de dire que le langage nécessite que l'opération soit effectuée, et seul le résultat est UB, et il est juste de dire que les guides de microarchitecture disent comment implémenter l'opération, et que la spécification ISA dit que ces opérandes font exception. Donc, il y a encore une histoire à raconter. – Potatoswatter

0

Vous ne nous montrez pas où 'intervalle' est défini. L'optimiseur peut faire quelque chose qui définit « intervalle » à 0. Pour changer votre code à

for(int i = 0; i < n; ++i) { 
    if (0==interval) { break; } 
    if((i + 1) % interval == 0) { // exception here 
    DoSomething(); 
    } 
} 

Et voir si vous obtenez toujours l'erreur. Ou mieux encore, montrez-nous où 'intervalle' prend sa valeur.

+0

'interval' est un' const int 'qui est défini au début le programme, à partir de la lecture des arguments de la ligne de commande. Donc, il est fondamentalement mis à zéro à partir de la ligne de commande. – Frank

+0

erreur: affectation de la variable en lecture seule 'intervalle' –

+0

@dwelch: Vous êtes un mauvais compilateur si vous lancez une erreur d'affectation sur 'const int interval = x;' – Frank

1

Pliage constant.

Vous avez déclaré un intervalle comme const int international et le compilateur vous a pris au mot. Pouvez-vous donner un exemple démontrant le problème?

+0

Que voulez-vous dire? Notez que le compilateur ne peut pas savoir si 'intervalle' sera nul ou pas puisque je l'ai défini en fonction d'un commutateur de ligne de commande. – Frank

+0

Veuillez vérifier la différence entre 'const int' et Integral Constant Expressions. Tout 'const int' n'est pas un ICE. – MSalters

+0

Il a dit que c'était un "const int" dans un autre commentaire. Un int int dans le même fichier source est autorisé à être une expression constante intégrale. – Joshua

0

Si l'optimisation modifie les résultats, vous devez désassembler le code et comparer la différence. Quelle est votre plateforme cible? x86, bras, ppc? système opérateur? etc?

 
#include 
const int interval=BOB; 
int main (void) 
{ 
    int i,n; 
    n=10; 
    for(i = 0; i < n; ++i) 
    { 
     if((i + 1) % interval == 0) 
     { // exception here 
      printf("%d\n",i); 
     } 
    } 
    return(0); 
} 
 
gcc interval.c -DBOB=0 -O2 -o interval 
interval.c: In function ‘main’: 
interval.c:15: warning: division by zero 

compilateur figured it out ...

EDIT:

Si vous essayez d'assigner à partir d'un argument de ligne de commande, vous devriez obtenir une erreur de compilation à la suite il ny a pas quoi que ce soit éxécuter.

 
#include <stdio.h> 
const int interval; 
int main (int argc, char *argv[]) 
{ 
    int i,n; 
    if(argc<2) return(1); 
    interval=atoi(argv[1]); 

    n=10; 
    for(i = 0; i < n; ++i) 
    { 
     if((i + 1) % interval == 0) 
     { // exception here 
      printf("%d\n",i); 
     } 
    } 
    return(0); 
} 
 
gcc -o interval interval.c 
interval.c: In function ‘main’: 
interval.c:7: error: assignment of read-only variable ‘interval’ 

S'il vous plaît donner un exemple complet.

Il est tout à fait possible que l'utilisation de const et de faire fonctionner le compilateur signifie que la variable est tirée de la mauvaise adresse et que tout ce qui s'y trouve peut être nul ou nul en fonction de cette adresse. reste de votre code. changer les paramètres d'optimisation se déplace où cette adresse est ou à quoi elle pointe ou à quoi elle est changée pendant l'exécution jusqu'à ce point changer les résultats.

EDIT:

 
#include <stdio.h> 
int main (int argc, char *argv[]) 
{ 
const int interval; 
    int i,n; 
    if(argc<2) return(1); 
    interval=atoi(argv[1]); 

    n=10; 
    for(i = 0; i < n; ++i) 
    { 
     if((i + 1) % interval == 0) 
     { // exception here 
      printf("%d\n",i); 
     } 
    } 
    return(0); 
} 

 
gcc -c interval.c 
interval.c: In function ‘main’: 
interval.c:7: error: assignment of read-only variable ‘interval’ 

Le compilateur sait encore qu'il est une variable en lecture seule, en utilisant l'adresse pour pointer une variable non-const à elle ne change pas son état en lecture seule, vient de se débarrasser de l'erreur du compilateur et échoue toujours à long terme. Comme conçu si par exemple .text est placé dans la mémoire morte (ROM/flash), peu importe le nombre de jeux d'adressage et de pointeurs que vous jouez, vous ne pourrez pas changer le temps d'exécution, jusqu'à ce que vous enleviez const écrire une variable.La manipulation de pointeur comme ceci est de toute façon un péché cardinal car elle peut échouer si et quand vous optimisez (si vous utilisez un très bon compilateur et pas forcément gcc, bien qu'il échoue également sur gcc) (99.999999999999% du temps c'est la chance que cela fonctionne, mais très explicable quand il échoue et pointe vers la conception du logiciel pas le compilateur ou la langue). À moins que le const soit la cause première de cette question, supprimez simplement le const et donnez-nous un exemple complet qui illustre le problème. Dans l'après-midi ou le jour, cela pourrait être fermé.

EDIT 2:

 
unsigned int fun (unsigned int a) 
{ 
    const unsigned int b = 7; 
    *(unsigned int *)&b = 5; 
    return(a+b); 
} 

compilez ci-dessus avec l'optimisation et vous obtenez:

 
    .global fun 
fun: 
    add r0, r0, #7 
    bx lr 

comme prévu, le fait const b en lecture seule. sans const:

 
unsigned int fun (unsigned int a) 
{ 
    unsigned int b = 7; 
    *(unsigned int *)&b = 5; 
    return(a+b); 
} 
 
    .global fun 
fun: 
    add r0, r0, #5 
    bx lr 

Ce que je suis surpris par mais jamais le moins montre comment fonctionne const.

+0

Non non, la valeur 'interval' n'a pas été définie de cette façon en utilisant' -D', elle a été définie en analysant et en traitant les arguments d'exécution du programme. – Frank

+0

si vous déclarez une variable const alors vous ne pouvez pas le changer dans le programme, votre compilateur devrait vous donner un avertissement vous indiquant qu'il est faux, par exemple: error: affectation de la variable en lecture seule 'interval', vous devez poster un Par exemple, sans de telles erreurs pour nous de résoudre le vrai problème. –

+0

Vous pouvez changer la valeur avec ce code: * (int *) & const_int_var = 0; Ne faites pas cela pour les globals: le compilateur est autorisé à placer des variables const globales dans la mémoire en lecture seule. – Joshua

Questions connexes