Je peux voir deux 'some'
littéraux dans le code assembleur généré par MSVC, mais un seul avec clang et gcc. Cela conduit à des résultats totalement différents de l'exécution du code.
static const char *A = "some";
static const char *B = "some";
void f() {
if (A == B) {
throw "Hello, string merging!";
}
}
Quelqu'un peut-il expliquer la différence et les similitudes entre ces sorties de compilation? Pourquoi clang / gcc optimise-t-il quelque chose même si aucune optimisation n'est demandée? Est-ce une sorte de comportement indéfini?
Je remarque également que si je change les déclarations pour celles présentées ci-dessous, clang / gcc / msvc n'en laisse aucune "some"
dans le code assembleur. Pourquoi le comportement est-il différent?
static const char A[] = "some";
static const char B[] = "some";
c++
language-lawyer
string-literals
string-interning
Eugène Kosov
la source
la source
Réponses:
Ce n'est pas un comportement indéfini, mais un comportement non spécifié. Pour les littéraux de chaîne ,
Cela signifie que le résultat de
A == B
pourrait êtretrue
oufalse
, sur lequel vous ne devriez pas dépendre.D'après la norme, [lex.string] / 16 :
la source
Les autres réponses expliquent pourquoi vous ne pouvez pas vous attendre à ce que les adresses des pointeurs soient différentes. Pourtant, vous pouvez facilement réécrire ceci d'une manière qui garantit cela
A
etB
ne pas comparer égal:La différence étant que
A
etB
sont maintenant des tableaux de caractères. Cela signifie qu'ils ne sont pas des pointeurs et que leurs adresses doivent être distinctes, tout comme celles de deux variables entières devraient l'être. C ++ confond cela car il rend les pointeurs et les tableaux interchangeables (operator*
etoperator[]
semblent se comporter de la même manière), mais ils sont vraiment différents. Par exemple, quelque chose commeconst char *A = "foo"; A++;
est parfaitement légal, maisconst char A[] = "bar"; A++;
ne l'est pas.Une façon de penser à la différence est que
char A[] = "..."
dit "donnez-moi un bloc de mémoire et remplissez-le avec les caractères...
suivis de\0
", alors quechar *A= "..."
dit "donnez-moi une adresse à laquelle je peux trouver les caractères...
suivis de\0
".la source
*p
etp[0]
non seulement "semblent se comporter de la même manière", mais par définition sont identiques (à condition que cep+0 == p
soit une relation d'identité parce que0
c'est l'élément neutre dans l'addition pointeur-entier). Après tout,p[i]
est défini comme*(p+i)
. La réponse fait cependant un bon point.typeof(*p)
ettypeof(p[0])
sont les deux,char
donc il ne reste vraiment pas grand-chose qui pourrait être différent. Je suis d'accord que «semblent se comporter de la même manière» n'est pas la meilleure formulation, car la sémantique est si différente. Votre message m'a rappelé la meilleure façon d'éléments d'accès des réseaux de C:0[p]
,1[p]
,2[p]
etc. Voici comment les pros le faire, au moins quand ils veulent confondre les gens qui sont nés après le langage de programmation C.Le fait qu'un compilateur choisisse ou non d'utiliser le même emplacement de chaîne pour
A
etB
dépend de l'implémentation. Officiellement, vous pouvez dire que le comportement de votre code n'est pas spécifié .Les deux choix implémentent correctement le standard C ++.
la source
Il s'agit d'une optimisation pour économiser de l'espace, souvent appelée "regroupement de chaînes". Voici la documentation pour MSVC:
https://msdn.microsoft.com/en-us/library/s0s0asdt.aspx
Par conséquent, si vous ajoutez / GF à la ligne de commande, vous devriez voir le même comportement avec MSVC.
Au fait, vous ne devriez probablement pas comparer des chaînes via des pointeurs comme celui-ci, tout outil d'analyse statique décent signalera ce code comme défectueux. Vous devez comparer ce qu'ils pointent, pas les valeurs réelles du pointeur.
la source