web-dev-qa-db-fra.com

Le stockage des mêmes littéraux de chaîne de contenu est-il garanti identique?

Le code ci-dessous est-il sûr? Il pourrait être tentant d'écrire du code semblable à ceci:

#include <map>

const std::map<const char*, int> m = {
    {"text1", 1},
    {"text2", 2}
};

int main () {
    volatile const auto a = m.at("text1");
    return 0;
}

La carte est destinée à être utilisée uniquement avec des littéraux de chaîne.

Je pense que c'est parfaitement légal et semble fonctionner, mais je n'ai jamais vu une garantie que le pointeur pour le littéral utilisé dans deux endroits différents soit le même. Je ne parvenais pas à faire compiler générer deux pointeurs distincts pour les littéraux avec le même contenu, alors j'ai commencé à me demander à quel point l'hypothèse est ferme.

Je veux seulement savoir si les littéraux avec le même contenu peuvent avoir des pointeurs différents. Ou plus formellement, le code ci-dessus peut-il exister?

Je sais qu'il existe un moyen d'écrire du code pour être sûr qu'il fonctionne, et je pense que l'approche ci-dessus est dangereuse car le compilateur pourrait décider d'affecter deux stockages différents pour le littéral, surtout s'ils sont placés dans des unités de traduction différentes. Ai-je raison?

24
luk32

La norme ne garantit pas que les adresses des littéraux de chaîne ayant le même contenu seront identiques. En fait, [Lex.string]/16 dit:

Si tous les littéraux de chaîne sont distincts (c'est-à-dire qu'ils sont stockés dans des objets qui ne se chevauchent pas) et si les évaluations successives d'un littéral de chaîne donnent le même objet ou un objet différent n'est pas spécifié.

La deuxième partie dit même que vous pourriez ne pas obtenir la même adresse lorsqu'une fonction contenant un littéral de chaîne est appelée une deuxième fois! Bien que je n'aie jamais vu un compilateur faire ça.

Ainsi, l'utilisation du même objet tableau de caractères lorsqu'un littéral de chaîne est répété est une optimisation de compilateur facultative. Avec mon installation de g ++ et des drapeaux de compilation par défaut, je trouve également que j'obtiens la même adresse pour deux littéraux de chaîne identiques dans la même unité de traduction. Mais comme vous l'avez deviné, j'en reçois différents si le même contenu littéral de chaîne apparaît dans différentes unités de traduction.


Un point intéressant connexe: il est également permis pour différents littéraux de chaîne d'utiliser des tableaux qui se chevauchent. Autrement dit, étant donné

const char* abcdef = "abcdef";
const char* def = "def";
const char* def0gh = "def\0gh";

il est possible que vous trouviez abcdef+3, def et def0gh sont tous le même pointeur.

En outre, cette règle de réutilisation ou de chevauchement des objets littéraux de chaîne s'applique uniquement à l'objet tableau sans nom directement associé au littéral, utilisé si le littéral se désintègre immédiatement en un pointeur ou est lié à une référence au tableau. Un littéral peut également être utilisé pour initialiser un tableau nommé, comme dans

const char a1[] = "XYZ";
const char a2[] = "XYZ";
const char a3[] = "Z";

Voici les objets du tableau a1, a2 et a3 sont initialisés à l'aide du littéral, mais sont considérés comme distincts du stockage littéral réel (si un tel stockage existe même) et suivent les règles d'objet ordinaires, de sorte que le stockage de ces tableaux ne se chevauchera pas.

18
aschepler

Le fait de savoir si deux littéraux de chaîne ayant exactement le même contenu sont exactement le même objet n'est pas spécifié et, à mon avis, il est préférable de ne pas s'y fier. Pour citer la norme:

[Lex.string]

16 L'évaluation d'un littéral de chaîne donne un objet littéral de chaîne avec une durée de stockage statique, initialisé à partir des caractères donnés comme spécifié ci-dessus. Si tous les littéraux de chaîne sont distincts (c'est-à-dire qu'ils sont stockés dans des objets qui ne se chevauchent pas) et si les évaluations successives d'un littéral de chaîne donnent le même objet ou un objet différent n'est pas spécifié.

Si vous souhaitez éviter les frais généraux de std::string, vous pouvez écrire un type de vue simple (ou utiliser std::string_view en C++ 17) qui est un type de référence sur un littéral de chaîne. Utilisez-le pour faire des comparaisons intelligentes au lieu de vous fier à votre identité littérale.

20
StoryTeller

Non, la norme C++ n'offre aucune garantie de ce type.

Cela dit, si le code est dans la même unité de traduction, il serait difficile de trouver un contre-exemple. Si main() est dans une traduction différente, un exemple de compteur pourrait être plus facile à produire.

Si la carte se trouve dans une bibliothèque liée dynamique ou un objet partagé différent, ce n'est certainement pas le cas.

Le qualificatif volatile est un hareng rouge.

5
Bathsheba

La norme C++ ne nécessite pas d'implémentation pour dédoublonner les littéraux de chaîne.

Lorsqu'un littéral de chaîne réside dans une autre unité de traduction ou une autre bibliothèque partagée qui nécessiterait l'éditeur de liens (ld) ou runtime-linker (ld.so) pour effectuer la déduplication littérale de la chaîne. Ce qu'ils ne font pas.

3
Maxim Egorushkin