Pourquoi est-il possible d’affecter un const char * à un char *?

Je sais que, par exemple, "hello" est de type const char* . Donc mes questions sont:

  1. Comment pouvons-nous assigner une chaîne littérale telle que "hello" à un const char* non const char* comme ceci:

     char* s = "hello"; // "hello" is type of const char* and s is char* // and we know that conversion from const char* to // char* is invalid 
  2. Une chaîne littérale telle que "hello" prend-elle de la mémoire dans tout mon programme ou est-elle simplement une variable temporaire qui sera détruite à la fin de l’instruction?

En fait, "hello" est de type char const[6] .

Mais l’essentiel de la question est toujours vrai: pourquoi le C ++ nous permet-il d’affecter un emplacement mémoire en lecture seule à un type non const ?

La seule raison à cela est la compatibilité en amont avec l’ancien code C, qui ne connaissait pas const . Si C ++ avait été ssortingct ici, cela aurait cassé beaucoup de code existant.

Cela dit, la plupart des compilateurs peuvent être configurés pour avertir du code comme obsolète, ou même le faire par défaut. De plus, C ++ 11 ne l’autorise pas du tout, mais les compilateurs ne l’appliquent peut-être pas encore.


Pour les fans de Standerdese:
[Ref 1] C ++ 03 Standard: §4.2 / 2

Un littéral de chaîne (2.13.4) qui n’est pas un littéral de chaîne large peut être converti en une valeur rvalue de type «pointeur vers caractère»; un littéral de chaîne large peut être converti en une valeur de type «pointeur sur wchar_t». Dans les deux cas, le résultat est un pointeur sur le premier élément du tableau. Cette conversion est prise en compte uniquement lorsqu’il existe un type de cible de pointeur approprié explicite, et non lorsqu’il est généralement nécessaire de convertir une valeur lvalue en une valeur rvalue. [ Remarque: cette conversion est obsolète . Voir l’Annexe D.] Dans le but de classer la résolution de surcharge (13.3.3.1.1), cette conversion est considérée comme une conversion de tableau à pointeur suivie d’une conversion de qualification (4.4). [Exemple: “abc” est converti en “pointeur vers const car” en tant que conversion de tableau à pointeur, puis en “pointeur en car” en tant que conversion de qualification. ]

C ++ 11 supprime simplement la citation ci-dessus, ce qui implique qu’il s’agit d’un code illégal en C ++ 11.

[Ref 2] C99 standard 6.4.5 / 5 “Littéraux de chaîne – Sémantique”:

Dans la phase de traduction 7, un octet ou un code de valeur zéro est ajouté à chaque séquence de caractères multi-octets résultant d’un littéral de chaîne ou de littéraux. La séquence de caractères multi-octets est ensuite utilisée pour initialiser un tableau de durée et de longueur de stockage statique juste suffisante pour contenir la séquence. Pour les littéraux de chaîne de caractères, les éléments du tableau ont le type char et sont initialisés avec les octets individuels de la séquence de caractères multi-octets; pour les littéraux de chaîne large, les éléments du tableau ont le type wchar_t et sont initialisés avec la séquence de caractères larges …

Il n’est pas précisé si ces tableaux sont distincts à condition que leurs éléments aient les valeurs appropriées. Si le programme tente de modifier un tel tableau, le comportement n’est pas défini.

Cette chaîne littérale telle que “hello” prendra de la mémoire dans tout mon programme, tout comme une variable temporaire qui sera détruite à la fin de l’instruction.

Il est conservé dans les données du programme, il est donc accessible pendant la durée de vie du programme. Vous pouvez renvoyer des pointeurs et des références à ces données à partir de la scope actuelle.

La seule raison pour laquelle const char* est converti en char* est la compatibilité avec c, comme les appels système de Winapi. Et ce casting est rendu inexplicable comme aucun autre casting constant.

Il suffit d’utiliser une ssortingng :

 std::ssortingng s("hello"); 

Ce serait la voie C ++. Si vous devez vraiment utiliser char , vous devrez créer un tableau et copier le contenu dessus.

La réponse à votre deuxième question est que la variable s est stockée dans la RAM en tant que type pointeur sur caractère. Si elle est globale ou statique, elle est allouée sur le tas et y rest pour toute la durée du programme en cours. S’il s’agit d’une variable locale (“auto”), elle est allouée sur la stack et y rest jusqu’au retour de la fonction actuelle. Dans les deux cas, il occupe la quantité de mémoire nécessaire pour contenir un pointeur.

La chaîne "Hello" est une constante et elle est stockée dans le programme lui-même, avec toutes les autres constantes et initialiseurs. Si vous avez construit votre programme pour qu’il s’exécute sur une appliance, la chaîne serait stockée dans la ROM.

Notez que, comme la chaîne est constante et que s est un pointeur, aucune copie n’est nécessaire. Le pointeur s indique simplement où la chaîne est stockée.

Dans votre exemple, vous n’atsortingbuez pas, mais vous construisez. std :: ssortingng, par exemple, a un constructeur std::ssortingng(const char *) (en réalité c’est plus compliqué, mais peu importe). De même, char * (s’il s’agissait d’un type plutôt que d’un pointeur sur un type) pourrait avoir un constructeur const char *, qui copie la mémoire.

Je ne sais pas vraiment comment le compilateur fonctionne vraiment ici, mais je pense que cela pourrait être similaire à ce que j’ai décrit ci-dessus: une copie de "Hello" est construite dans la stack et s est initialisée avec l’adresse de cette copie.