En ajoutant un peu à la réponse de Christian Gibbons...
En C, les littéraux de chaîne, comme "Bonjour tout le monde!"
, sont stockés dans des tableaux de char
de sorte qu'ils sont visibles pendant toute la durée de vie du programme. Les littéraux de chaîne sont censés être immuables, et certaines implémentations les stockeront dans un segment de mémoire en lecture seule (de sorte que toute tentative de modifier le contenu du littéral déclenchera une erreur d'exécution). Certaines implémentations ne le font pas, et tenter de modifier le contenu du littéral peut ne pas déclencher d'erreur d'exécution (cela peut même sembler fonctionner comme prévu). La définition du langage C laisse le comportement "indéfini" afin que le compilateur soit libre de gérer la situation comme il le souhaite.
En C++, les littéraux de chaîne sont stockés dans des tableaux de _const_ char
, de sorte que toute tentative de modifier le contenu du littéral déclenchera un diagnostic au moment de la compilation.
Comme le souligne Christian, le mot clé const
n'était pas à l'origine une partie de C. Il faisait cependant initialement partie de C++, et il rend l'utilisation des littéraux de chaîne un peu plus sûre.
Rappelez-vous que le mot clé const
ne signifie pas "stocker ceci dans une mémoire en lecture seule", cela signifie seulement "cette chose ne peut pas être la cible d'une assignation".
Rappelez-vous également que, à moins qu'il ne soit l'opérande des opérateurs sizeof
ou unaire *
, ou qu'il ne s'agisse d'un littéral de chaîne utilisé pour initialiser un tableau de caractères dans une déclaration, une expression de type "tableau de N éléments de T
" sera convertie ("déclinée") en une expression de type "pointeur vers T
" et la valeur de l'expression sera l'adresse du premier élément du tableau.
En C++, lorsque vous écrivez
const char *str = "Bonjour tout le monde";
l'adresse du premier caractère de la chaîne est stockée dans str
. Vous pouvez faire en sorte que str
pointe vers un autre littéral de chaîne :
str = "Adieu monde cruel";
mais ce que vous ne pouvez pas faire, c'est modifier le contenu de la chaîne, quelque chose comme
str[0] = 'h';
ou
strcpy( str, "Quelque chose d'autre" );
26 votes
Alors qu'en C, ce n'est pas le cas Vous avez tort. Les littéraux de chaîne en C sont constants, mais ils peuvent être pointés par un
char*
. Vous ne pouvez toujours pas modifier la chaîne via ce pointeur. En C++, ils ont simplement éliminé cette exception à la const-correctness pour éviter la confusion et les erreurs.5 votes
Parce que c'est ainsi qu'ils ont conçu le langage. C n'avait pas initialement le mot-clé
const
, donc cela casserait du code hérité s'ils changeaient les littéraux pour nécessiter une qualificationconst
après l'introduction du mot-clé. Les littéraux de chaîne de C sont immuables, cependant, donc changer le contenu est un comportement non défini même s'il n'est pas qualifié deconst
.0 votes
@ChristianGibbons alors pourquoi le code suivant fonctionne-t-il ? ``char* str = "Hello World"; str = "Goodbye World";```
13 votes
@Serket: Ce n'est pas modifier directement la chaîne de caractères elle-même ; c'est changer le pointeur
str
(qui n'est pas une constante) pour pointer vers une autre chaîne.char *str = "Bonjour le monde"; str[0] = 'J';
serait un comportement non défini.1 votes
@NateEldredge Merci! Pourrait quelqu'un publier une réponse officielle pour que je puisse la marquer comme correcte?
1 votes
@FrançoisAndrieux vous devriez probablement copier cela dans une réponse.
0 votes
@FrançoisAndrieux Les littéraux de chaîne en C ne sont pas nécessairement constants. Tenter de modifier un littéral de chaîne (plus précisément, l'objet tableau anonyme auquel un littéral de chaîne fait référence) a un comportement indéfini. Une implémentation C conforme pourrait rendre ce tableau modifiable.
0 votes
@KeithThompson Vous pouvez dire cela pour à peu près n'importe quel exemple de comportement indéfini. C'est constant en ce qui concerne la norme.
0 votes
@BessieTheCow: Pour qu'une chose soit constante selon la norme, tenter de la modifier serait une contrainte ou une violation de syntaxe. Par exemple,
const int *ptr = ...; *ptr = 42;
doit être diagnostiqué.char *s = "hello"; *s = 'H';
ne nécessite pas de diagnostic. La chaîne de caractères littérale est de typechar[6]
, pasconst char[6]
(comme c'est le cas en C++). (Notez également que "constant" et "const
" ne sont pas la même chose. "Constant" signifie que la valeur est déterminée au moment de la compilation.const
signifie en lecture seule. Les chaînes de caractères littérales en C++ sontconst
.0 votes
@KeithThompson Que diriez-vous de
const int x = 5; *(int*)&x = 42;
ouconstexpr int x = 5; *(int*)&x = 42;
? C'est UB aucun diagnostic requis autant que je sache.0 votes
@BessieTheCow Bien sûr, vous pouvez toujours vous débarrasser de
const
et éviter un diagnostic requis. Le point est que les littéraux de chaîne ne sont pasconst
en premier lieu. Il y a trois règles distinctes en jeu : (a) Modifier un objet via un lvalue qualifiéconst
est une violation de contrainte. (b) Modifier un objet défini avecconst
a un comportement indéterminé (c) Modifier un littéral de chaîne a un comportement indéterminé.0 votes
@BessieTheCow La seule raison pour laquelle la modification des littéraux de chaîne est traitée séparément est d'éviter de casser le code existant. Rendre les littéraux de chaîne
const
aurait entraîné une violation de contrainte pourchar *s = "hello";
. Tel que c'est actuellement, c'est parfaitement valide (mais déconseillé) et invoque un comportement indéfini seulement si vous essayez de modifier l'objets
pointe vers.