Différence entre char * et const char *?

177

Quelle est la différence entre

char* name

qui pointe vers un littéral de chaîne constant, et

const char* name
Iceman
la source
qu'entendez-vous par " littéral de chaîne constante " en C (et non en C ++)
gbulmer
1
... char * name peut être fait pour pointer vers un littéral de chaîne constant
Iceman
la constante dans "constant string literal" est redondante, puisque tous les littéraux de chaîne sont en théorie des entités constantes. C'est le contenu de la variable qui peut être rendu constant ou mutable. La déclaration "const" lèvera simplement une erreur de compilation si vous essayez de changer le contenu du caractère pointé par "name"
Cupcake
Simple: le nom "char * name" est un pointeur vers char, c'est-à-dire que les deux peuvent être modifiés ici. "const char * name" name est un pointeur vers const char ie le pointeur peut changer mais pas char.
akD
Lisez ces choses de droite à gauche.
Jiapeng Zhang

Réponses:

406

char*est un pointeur mutable vers un caractère / une chaîne mutable .

const char*est un pointeur mutable vers un caractère / une chaîne immuable . Vous ne pouvez pas modifier le contenu des emplacements vers lesquels ce pointeur pointe. En outre, les compilateurs doivent envoyer des messages d'erreur lorsque vous essayez de le faire. Pour la même raison, la conversion de const char *en char*est obsolète.

char* constest un pointeur immuable (il ne peut pointer vers aucun autre emplacement) mais le contenu de l'emplacement vers lequel il pointe est modifiable .

const char* constest un pointeur immuable vers un caractère / une chaîne immuable .

ankit.karwasra
la source
4
La confusion peut être dissipée avec l'utilisation d'une variable après les déclarations mentionnées ci-dessus et en faisant référence à cette variable.
ankit.karwasra
3
@ ankit.karwasra, Vous en avez manqué un de plus:char const *
Pacerier
Je suppose que deux options avec un caractère / une chaîne mutable sont très dangereuses, car vous pourriez faire une mémoire de défauts de segmentation, et si vous êtes vraiment intelligent, vous pouvez pirater l'ordinateur. C'est pourquoi les compilateurs ont toujours montré des avertissements dans ces implémentations, je pense
Daniel N.
1
La mutation ne char *donnera- t-elle pas une erreur de segmentation pendant l'exécution?
Divyanshu Maithani
1
Donc, j'utilise constsi je veux que le compilateur donne une erreur si j'ai oublié et changé les données par erreur, non?
Accountant م
43
char *name

Vous pouvez changer le caractère vers quels namepoints, ainsi que le caractère vers lequel il pointe.

const char* name

Vous pouvez changer le caractère vers quels namepoints, mais vous ne pouvez pas modifier le caractère vers lequel il pointe.
correction: vous pouvez modifier le pointeur, mais pas le caractère vers lequel namepointe ( https://msdn.microsoft.com/en-us/library/vstudio/whkd4k6a(v=vs.100).aspx , voir "Exemples" ). Dans ce cas, le constspécificateur s'applique à char, pas l'astérisque.

Selon la page MSDN et http://en.cppreference.com/w/cpp/language/declarations , l' constavant *fait partie de la séquence de déclinaison, tandis que l' constaprès *fait partie du déclarateur.
Une séquence de spécificateur de déclaration peut être suivie par plusieurs déclarateurs, c'est pourquoi const char * c1, c2déclare c1as const char *et c2as const char.

ÉDITER:

D'après les commentaires, votre question semble porter sur la différence entre les deux déclarations lorsque le pointeur pointe vers une chaîne littérale.

Dans ce cas, vous ne devez pas modifier le caractère vers lequel namepointe, car cela pourrait entraîner un comportement indéfini . Les littéraux de chaîne peuvent être alloués dans des régions de mémoire en lecture seule (implémentation définie) et un programme utilisateur ne doit en aucun cas le modifier. Toute tentative de le faire entraîne un comportement indéfini.

La seule différence dans ce cas (d'utilisation avec des chaînes littérales) est que la deuxième déclaration vous donne un léger avantage. Les compilateurs vous donneront généralement un avertissement au cas où vous tenteriez de modifier la chaîne littérale dans le second cas.

Exemple d'exemple en ligne:

#include <string.h>
int main()
{
    char *str1 = "string Literal";
    const char *str2 = "string Literal";
    char source[] = "Sample string";

    strcpy(str1,source);    //No warning or error, just Undefined Behavior
    strcpy(str2,source);    //Compiler issues a warning

    return 0;
}

Production:

cc1: les avertissements étant traités comme des erreurs
prog.c: Dans la fonction 'main':
prog.c: 9: erreur: la transmission de l'argument 1 de 'strcpy' élimine les qualificatifs du type de cible du pointeur

Notez que le compilateur avertit pour le deuxième cas mais pas pour le premier.

Alok Save
la source
Merci .. je mixais avec la chaîne littérale constante, qui est définie comme: char * name = "String Literal"; La modification de "String Literal" n'est pas définie.
Iceman
@ user1279782: Euh, attendez! Parlez-vous de pointes pointant vers des chaînes littérales ici? Dans ce cas, vous ne devez pas modifier le caractère auquel les namepoints dans les deux cas, cela pourrait entraîner UB.
Alok Enregistrer
Oui, c'était le point. Donc, dans ce cas, char * name et const char * name se comportent de la même manière, non?
Iceman
4
Cette réponse est soit extrêmement ambiguë, soit tout simplement fausse. J'interpréterais "Vous ne pouvez pas changer le caractère vers lequel pointe le nom, mais vous pouvez modifier le caractère vers lequel il pointe." Ne pas être en mesure de modifier le pointeur lui - même, mais être capable de modifier l'emplacement de mémoire il pointe, ce qui est incorrect: ideone.com/6lUY9s alternativement pour C pur: ideone.com/x3PcTP
shroudednight
1
@shroudednight: Vous devez en savoir un peu plus sur les comportements non définis et faire la distinction entre: autorisé et ne doit pas être fait. :)
Alok Sauvegardez le
16
char mystring[101] = "My sample string";
const char * constcharp = mystring; // (1)
char const * charconstp = mystring; // (2) the same as (1)
char * const charpconst = mystring; // (3)

constcharp++; // ok
charconstp++; // ok
charpconst++; // compile error

constcharp[3] = '\0'; // compile error
charconstp[3] = '\0'; // compile error
charpconst[3] = '\0'; // ok

// String literals
char * lcharp = "My string literal";
const char * lconstcharp = "My string literal";

lcharp[0] = 'X';      // Segmentation fault (crash) during run-time
lconstcharp[0] = 'X'; // compile error

// *not* a string literal
const char astr[101] = "My mutable string";
astr[0] = 'X';          // compile error
((char*)astr)[0] = 'X'; // ok
Afriza N. Arief
la source
1
Aucun de vos pointeurs ne pointe vers des "littéraux de chaîne constante" selon la question.
caf
Il convient de noter que la modification de la char *valeur donne une erreur de segmentation puisque nous essayons de modifier une chaîne littérale (qui est présente en mémoire morte)
Divyanshu Maithani
10

Dans les deux cas, vous ne pouvez pas modifier un littéral de chaîne, que le pointeur vers ce littéral de chaîne soit déclaré char *ou non const char *.

Cependant, la différence est que si le pointeur est, const char *le compilateur doit donner un diagnostic si vous essayez de modifier la valeur pointée, mais si le pointeur est, char *il ne le fait pas.

caf
la source
1
"Dans les deux cas, vous ne pouvez pas modifier une chaîne littérale, indépendamment du fait que ... [elle] soit déclarée comme char * ou const char *" Je suis d'accord que le programmeur ne devrait pas essayer, mais dites-vous que chaque compilateur C, sur chaque plate-forme rejettera le code, fera en sorte que le code échoue au moment de l'exécution, ou autre chose? Je crois qu'un fichier pourrait avoir la définition et l'initialisation, et un autre fichier pourrait contenir extern ... nameet avoir *name = 'X';. Sur le «bon système d'exploitation», cela pourrait échouer, mais sur les systèmes embarqués, je m'attendrais à ce qu'il fasse quelque chose de spécifique à la plate-forme / au compilateur.
gbulmer
@gbulmer: vous ne pouvez pas modifier une chaîne littérale dans un programme C correct. Ce qu'un programme C incorrect qui essaie peut entraîner n'est ni ici ni là.
caf
@gbulmer: Une définition utile est un programme qui ne rompt aucune contrainte spécifiée par le standard du langage C. En d'autres termes, un programme qui modifie un littéral de chaîne est incorrect de la même manière qu'un programme qui déréférence un pointeur nul ou effectue une division par 0 est incorrect.
caf
caf - Je pensais que c'était peut-être ce que tu voulais dire. Alors "Dans aucun des cas, vous ne pouvez modifier un littéral de chaîne" semble être trop prononcé. Il serait exact de dire "Dans les deux cas, les contraintes spécifiées par le standard du langage C ont été brisées, peu importe ... Il n'est pas possible pour le compilateur ou le système d'exécution d'identifier les violations du standard dans tous les cas." Je suppose que la norme prend la position que l'effet n'est pas défini?
gbulmer
1
Lorsqu'une norme ne peut rien affirmer de toute façon, je pense que définir le comportement comme étant «indéfini» semble être exactement la bonne limite et utile. Pour affirmer la relation, un «programme C correct» « ne peut pas déréférencer un pointeur nul» semble équivalent à prouver le problème d'arrêt. Mais ça ne me dérange pas. Je ne le ferais pas et je m'attendais à m'en tirer 'scott free' :-)
gbulmer
4

CAS 1:

char *str = "Hello";
str[0] = 'M'  //Warning may be issued by compiler, and will cause segmentation fault upon running the programme

Ce qui précède définit str pour pointer vers la valeur littérale "Hello" qui est codée en dur dans l'image binaire du programme, qui est marquée comme lecture seule en mémoire, signifie que toute modification de ce littéral String est illégale et qui entraînerait des erreurs de segmentation.

CAS 2:

const char *str = "Hello";
str[0] = 'M'  //Compile time error

CAS 3:

char str[] = "Hello";
str[0] = 'M'; // legal and change the str = "Mello".
Mohit
la source
2

Le premier que vous pouvez réellement changer si vous le souhaitez, le second vous ne le pouvez pas. Renseignez-vous sur l' constexactitude (il existe de bons guides sur la différence). Il y a aussi char const * namelà où vous ne pouvez pas le rejoindre.

chikuba
la source
Qu'est-ce qui peut changer exactement?
Antti Haapala
2

La question est de savoir quelle est la différence entre

char *name

qui pointe vers un littéral de chaîne constant, et

const char *cname

Ie donné

char *name = "foo";

et

const char *cname = "foo";

Il n'y a pas beaucoup de différence entre les 2 et les deux peuvent être considérés comme corrects. En raison de la longue tradition du code C, les littéraux de chaîne ont eu un type de char[], non const char[], et il existe de nombreux codes plus anciens qui acceptent également à la char *place de const char *, même s'ils ne modifient pas les arguments.

La principale différence des 2 en général est que *cnameou cname[n]va s'évaluer à lvaleurs de type const char, alors que *nameou name[n]va s'évaluer à lvaleurs de type char, qui sont des lvaleurs modifiables . Un compilateur conforme est requis pour produire un message de diagnostic si la cible de l'affectation n'est pas une lvalue modifiable ; il n'a pas besoin de produire d'avertissement lors de l'affectation à des valeurs de type char:

name[0] = 'x'; // no diagnostics *needed*
cname[0] = 'x'; // a conforming compiler *must* produce a diagnostics message

Le compilateur n'est pas obligé d'arrêter la compilation dans les deux cas; il suffit qu'il produise un avertissement pour l'affectation à cname[0]. Le programme résultant n'est pas un programme correct . Le comportement de la construction n'est pas défini . Il peut planter, ou pire encore, il peut ne pas planter et changer la chaîne littérale en mémoire.

Antti Haapala
la source