2017-06-01 3 views
2

https://kukuruku.co/post/i-do-not-know-c/Comment fonctionne cet alias de pointeur?

Problème # 7:

#include <stdio.h> 
void f(int *i, long *l) 
{ 
    printf("1. v=%ld\n", *l); /* (1) */ 
    *i = 11;     /* (2) */ 
    printf("2. v=%ld\n", *l); /* (3) */ 
} 
int main() 
{ 
    long a = 10; 
    f((int *) &a, &a); 
    printf("3. v=%ld\n", a); 
    return 0; 
} 

sortie sur deux compilateurs différents sur un petit système endian est:

1. v=10 2. v=11 3. v=11 
1. v=10 2. v=10 3. v=11 

Comment est le deuxième résultat possible? Je n'ai pas tout à fait comment l'explication qui explique le résultat en se référant à l'aliasing strict. Est-ce que le compilateur ignore complètement la ligne (2)?

+2

Ce comportement est indéfini. * Upd: * BTW, regardé l'article, et * est * sur le comportement indéfini. Avez-vous lu ça? –

+0

Ma conjecture est que votre compilateur 2 (quoi qu'il puisse être) fait probablement une «optimisation prématurée» – Zakir

+6

@Zakir Les compilateurs ne sont pas sensibles à l'optimisation * prématurée .... –

Répondre

6

Citant Wikipedia:

en C ou C++, comme l'exige la règle stricte de aliasing, pointeur arguments dans une fonction sont supposés ne pas alias si elles pointent vers fondamentalement différents types, à l'exception pour char * et void *, qui peut alias à tout autre type. Certains compilateurs autorisent la désactivation de la règle d'aliasing strict , de sorte que tout argument de pointeur peut alias tous les autres arguments de pointeur . Dans ce cas, le compilateur doit supposer que tous les accès via ces pointeurs peuvent être alias. Cela peut empêcher certaines optimisations d'être effectuées.

C'est là que la règle est violée:

f((int *) &a, &a); 
    ^aliasing to different type (a is 'long') 
      ^passing the same variable with different type 

Le problème est que l'hypothèse des règles strictes d'aliasing, premier et second argument du point de fonction à un autre endroit car ceux-ci sont de différents types. Voilà pourquoi l'auteur explique:

Par conséquent, nous pouvons supposer que tout temps n'a pas changé.

Et ici: printf("2. v=%ld\n", *l); une valeur long est déréférencé.

C'est pourquoi cette partie (2) est un comportement indéfini sur les deux compilateurs.

3

partie (2) est non ignorée ou supprimée par le compilateur, il est exécuté et la valeur de a dans le champ d'application de l'appelant est modifiée d'une certaine façon dépendant du système, mais le compilateur peut assurer cette fonction à l'intérieur de f(), la modification d'un int via le pointeur i ne modifie pas le long pointé par l, donc il peut réutiliser la valeur lue pour le premier printf() comme un argument pour la deuxième printf.

Le deuxième compilateur semble générer du code qui le fait tandis que le code généré par le premier compilateur relit la valeur pointée par l. En effet, les options du compilateur telles que les paramètres d'optimisation peuvent modifier ce comportement, ce qui est cohérent avec le standard C qui décrit ce code comme ayant un comportement indéfini.

+0

Le fait que les différents compilateurs ou les options du compilateur ayant le même compilateur aient un comportement différent n'est pas * une * justification pour décrire le code qui a ** un comportement non défini **. – curiousguy

+0

@curiousguy: bon point, réponse mise à jour. Je vous remercie. – chqrlie

0

Le deuxième résultat est rendu possible par le fait que la valeur de *l ne peut pas changer entre (1) et (3). (La règle stricte d'aliasing assure cela.)

Ainsi, le programme n'a besoin que de calculer sa valeur une fois, et le compilateur a généré code équivalent à

void f(int *i, long *l) 
{ 
    long l2 = *l; 
    printf("1. v=%ld\n", l2); /* (1) */ 
    *i = 11;     /* (2) */ 
    printf("2. v=%ld\n", l2); /* (3) */ 
}