Pourquoi les compilateurs permettent-ils que les littéraux de chaîne ne soient pas constants?

Et où sont les littéraux en mémoire exactement? (voir exemples ci-dessous)

Je ne peux pas modifier un littéral, alors ce serait censé être un caractère constant *, bien que le compilateur me permette d’utiliser un caractère *, je n’ai aucun avertissement, même avec la plupart des indicateurs du compilateur.

Alors qu’une conversion implicite d’un type const char * vers un type char * me donne un avertissement, voir ci-dessous (testé sur GCC, mais il se comporte de la même manière sur VC ++ 2010).

De plus, si je modifie la valeur d’un caractère constant (avec une astuce ci-dessous où GCC pourrait mieux me donner un avertissement), cela ne génère aucune erreur et je peux même le modifier et l’afficher sur GCC comportement indéfini, je me demande pourquoi il n’a pas fait la même chose avec le littéral). C’est pourquoi je demande où ces littéraux sont stockés, et où sont plus communément supposés stockés?

const char* a = "test"; char* b = a; /* warning: initialization discards qualifiers from pointer target type (on gcc), error on VC++2k10 */ char *c = "test"; // no comstack errors c[0] = 'p'; /* bus error when execution (we are not supposed to modify const anyway, so why can I and with no errors? And where is the literal stored for I have a "bus error"? I have 'access violation writing' on VC++2010 */ const char d = 'a'; *(char*)&d = 'b'; // no warnings (why not?) printf("%c", d); /* displays 'b' (why doesn't it do the same behavior as modifying a literal? It displays 'a' on VC++2010 */ 

La norme C n’interdit pas la modification de littéraux de chaîne. Cela indique simplement que le comportement n’est pas défini si la tentative est faite. Selon le raisonnement du C99, certains membres du comité souhaitaient que les littéraux de chaîne soient modifiables. La norme ne l’interdit donc pas explicitement.

Notez que la situation est différente en C ++. En C ++, les littéraux de chaîne sont des tableaux de const char. Cependant, C ++ autorise les conversions de const char * en char *. Cette fonctionnalité est cependant déconseillée.

Principalement des raisons historiques. Mais gardez à l’esprit qu’ils sont quelque peu justifiés: les littéraux de chaîne n’ont pas le type char * , mais char [N]N désigne la taille du tampon (sinon, sizeof ne fonctionnerait pas comme prévu pour les littéraux de chaîne) et peut être utilisé pour initialiser les tableaux non const . Vous ne pouvez les affecter qu’aux pointeurs const raison des conversions implicites de tableaux en pointeurs et de const à const .

Il serait plus cohérent que les chaînes de caractères présentent le même comportement que les chaînes composées, mais comme il s’agit d’une construction C99 et que la compatibilité avec les versions antérieures doit être maintenue, cette option n’est pas envisageable. Les littéraux de chaînes restnt donc un cas exceptionnel.

Et où sont les littéraux en mémoire exactement? (voir exemples ci-dessous)

Segment de données initialisé. Sous Linux, il s’agit de .data ou .rodata .

Je ne peux pas modifier un littéral, alors ce serait censé être un caractère constant *, bien que le compilateur me permette d’utiliser un caractère *, je n’ai aucun avertissement, même avec la plupart des indicateurs du compilateur.

Historique comme cela a déjà été expliqué par d’autres. La plupart des compilateurs vous permettent d’indiquer si les littéraux de chaîne doivent être en lecture seule ou modifiables avec une option de ligne de commande.

La raison pour laquelle on souhaite généralement avoir des littéraux de chaîne en lecture seule est que le segment avec des données en lecture seule en mémoire peut être (et est normalement) partagé entre tous les processus démarrés à partir de l’exécutable. Cela évite évidemment de gaspiller de la RAM pour conserver des copies redondantes de la même information.

Je ne suis pas certain de la signification des normes C / C ++ à propos des chaînes. Mais je peux dire exactement ce qui se passe réellement avec les littéraux de chaîne dans MSVC. Et, je crois, d’autres compilateurs se comportent de la même manière.

Les littéraux de chaîne résident dans une section de données const. Leur mémoire est mappée dans l’espace d’adressage du processus. Cependant, les pages de mémoire dans lesquelles elles sont stockées sont en lecture seule (à moins d’être explicitement modifiées lors de l’exécution).

Mais il y a quelque chose de plus que vous devriez savoir. Toutes les expressions C / C ++ contenant des guillemets n’ont pas la même signification. Clarifions tout.

 const char* a = "test"; 

L’instruction ci-dessus amène le compilateur à créer un littéral de chaîne “test”. L’éditeur de liens s’assure qu’il sera dans le fichier exécutable. Dans le corps de la fonction, le compilateur génère un code qui déclare une variable a sur la stack, qui est initialisée par l’adresse du littéral de chaîne “test”.

 char* b = a; 

Ici, vous déclarez une autre variable b sur la stack qui obtient la valeur de a . Puisqu’un pointait vers une adresse en lecture seule – il en serait de même b . Le fait que b ne possède pas de sémantique const ne signifie pas que vous pouvez modifier son contenu.

 char *c = "test"; // no comstack errors c[0] = 'p'; 

Ce qui précède génère une violation d’access. Encore une fois, le manque de const ne veut rien dire au niveau de la machine

 const char d = 'a'; *(char*)&d = 'b'; 

Tout d’abord, ce qui précède n’est pas lié aux littéraux de chaîne. ‘a’ n’est pas une chaîne. C’est un personnage. C’est juste un nombre. C’est comme écrire le texte suivant:

 const int d = 55; *(int*)&d = 56; 

Le code ci-dessus est un imbécile du compilateur. Vous dites que la variable est const , mais vous parvenez à la modifier. Mais ceci n’est pas lié à l’exception du processeur, puisque d réside néanmoins dans la mémoire en lecture / écriture.

J’aimerais append un cas supplémentaire:

 char b[] = "test"; b[2] = 'o'; 

Ce qui précède déclare un tableau sur la stack et l’initialise avec la chaîne “test”. Il réside dans la mémoire en lecture / écriture et peut être modifié. Il n’y a pas de problème ici.

Je n’ai aucun avertissement même avec la plupart des drapeaux du compilateur

Vraiment? Lorsque je comstack l’extrait de code suivant:

 int main() { char* p = "some literal"; } 

sur g ++ 4.5.0 même sans aucun drapeau , je reçois l’avertissement suivant:

warning: conversion obsolète de constante chaîne en ‘char *’

Vous pouvez écrire à c parce que vous ne l’avez pas fait. Définir c comme const serait une pratique correcte puisque le côté droit a le type const char* .

Il génère une erreur au moment de l’exécution car la valeur “test” est probablement atsortingbuée au segment de code en lecture seule. Voir ici et ici .