web-dev-qa-db-fra.com

Pointeurs et chaînes C++

J'apprends moi-même le C++ et je suis un peu confus au sujet des pointeurs (en particulier dans le code source suivant). Mais tout d’abord, je vais vous montrer ce que je sais (et ensuite opposer le code à cela parce que j’ai l’impression que des contradictions existent).

Ce que je sais:

int Age = 30;
int* pointer = &Age;

cout << "The location of variable Age is: " << pointer << endl;
cout << "The value stored in this location is: " << *pointer << endl;

Les pointeurs contiennent des adresses de mémoire. En utilisant l'opérateur indirection (déréférencement) (le *), vous pouvez accéder à ce qui est stocké dans l'emplacement de mémoire du pointeur. Sur le code de ce livre, j'ai du mal à comprendre ...

cout << "Enter your name: ";
string name;
getline(cin, name); //gets full line up to NULL terminating character

int CharsToAllocate = name.length() + 1; //calculates length of string input
                          //adds one onto it to adjust for NULL character
char* CopyOfName = new char[CharsToAllocate];
// pointer to char's called CopyOfName, is given the memory address of the 
//beginning of a block
//of memory enough to fit CharsToAllocate. Why we added 1? Because char's need a 
//NULL terminating character (\0)

strcpy(CopyOfName, name.c_str()); //copies the string name, into a pointer?

cout << "Dynamically allocated buffer contains: " << CopyOfName << endl;
delete[] CopyOfName; //always delete a pointer assigned by new to prevent memory leaks

Sortie:

Enter your name: Adam
Dynamically allocated buffer contains: Adam

Les commentaires dans le code ci-dessus sont mes commentaires. Mon problème commence par strcpy. Pourquoi name.c_str() est-il copié dans un pointeur CopyOfName? Cela signifie-t-il que toutes les chaînes sont des pointeurs essentiels? Donc, comme string testing = "Hello world"; Est-ce qu'un pointeur pointe vers l'emplacement de la mémoire où "H" est stocké?

Ensuite, pourquoi est-ce que dans l’instruction d’impression, on utilise CopyOfName et non *CopyOfName? Les pointeurs contiennent des adresses de mémoire? Utiliser *CopyOfName imprimerait le contenu de l’emplacement mémoire. J'ai essayé ceci dans Code :: Blocks et si le texte saisi était "Hello World". L'utilisation de *CopyOfName dans la déclaration d'impression donnerait simplement un "H". Cela a du sens depuis le moment où j'ai déclaré que j'avais besoin d'un bloc de mémoire avec la «nouvelle» chose, cela renvoie en fait un pointeur sur la première partie du bloc de mémoire alloué dynamiquement.

La seule façon de réconcilier cela est si une chaîne est en réalité un pointeur.

string testing = "Confused";
cout << testing << endl;

imprimerait le mot "confus"

Cependant, si j'essaie de compiler

string testing = "Confused";
cout << *testing; 

Je reçois un message d'erreur.

En résumé, pour résumer ma question, j’essaie de comprendre le code avec strcpy et l’énoncé cout.

13
DWade64

Il semble que vous compreniez ce que sont les chaînes de style C, mais pour résumer, ce ne sont que des tableaux de caractères en mémoire, terminés par convention par un caractère nul \0. Ils sont généralement référencés via un char* pointant vers la première lettre de la chaîne. Lorsqu'ils sont imprimés, les caractères de la chaîne sont généralement imprimés en commençant par le premier. L'impression (ou la copie, etc.) s'arrête lorsque le terminateur \0 est atteint.

Un std::string est une classe qui (généralement) enveloppe une chaîne de style C. Cela signifie qu'un objet std::string (généralement) possède une chaîne de style C privée utilisée pour implémenter ses fonctionnalités. La fonction std::string::c_str() renvoie un pointeur sur cette chaîne de style C sous-jacente.

Supposons que char *str; pointe vers une chaîne de style C. Si vous essayez d'exécuter cout << *str << endl;, vous remarquerez que seul le premier caractère est imprimé. Cela est dû à la surcharge de fonctions de C++. Le type de données *str est char. La version char de cout est appelée et imprime fidèlement le caractère unique *str. Pour assurer la compatibilité avec les chaînes de style C, la version de cout qui utilise un char* en tant qu’argument traite le pointeur comme une chaîne de style C à des fins d’impression. Si vous cout un int*, par exemple, la int sous-jacente ne sera pas imprimée.

Edit: Un autre commentaire:

La raison pour laquelle votre tentative de déréférencement d'un objet std::string a échoué est que ce n'est en réalité pas un pointeur. Vous pouvez déréférencer la valeur de retour de std::string::c_str() et récupérer le premier char de la chaîne.

Connexes: Comment std :: string est-il implémenté? .

16
Andrey Mishchenko

En C, les chaînes sont simplement des tableaux de caractères. Et les tableaux se décomposent en pointeurs lorsqu'ils sont utilisés comme arguments d'une fonction.

En C++, std::string est une classe. Il contient un tableau de caractères de style C et c’est ce que c_str() renvoie. Mais la chaîne elle-même n'est pas un pointeur, vous ne pouvez donc pas la déréférencer. vous devez utiliser la méthode c_str() pour obtenir un pointeur sur le contenu de la chaîne.

3
Barmar

Donc, comme string testing = "Hello world"; Est-ce réellement un pointeur pointant vers l'emplacement de la mémoire où "H" est stocké?

Non, au-dessus, vous avez un objet appelé string. Ce serait vrai pour char* testing = "Hello World". Comme vous pouvez le constater, il est même déclaré comme pointeur et pointe vers le premier caractère de la chaîne - H.

Ensuite, pourquoi la déclaration imprimée indique-t-elle que CopyOfName n'est pas *CopyOfName? Les pointeurs contiennent des adresses de mémoire? Utiliser *CopyOfName imprimerait le contenu de l’emplacement mémoire. J'ai essayé ceci dans des blocs de code et si le texte saisi était "Hello World". Utiliser *CopyOfName dans la déclaration imprimée donnerait simplement un "H"

cout prend le pointeur sur le premier caractère de la chaîne pour que CopyOfName soit correct. Dans ce cas, chaque caractère sera imprimé en commençant par H jusqu'à ce qu'il trouve\0 (caractère nul). Les chaînes comme "hello" ont en fait 6 caractères - 'h' 'e' 'l' 'l' '\' '0' Lorsque vous écrivez *CopyOfName, vous déréférenciez ce pointeur et *CopyOfName est en réalité un seul caractère

3
janepe

Répondre à vos questions dans l'ordre:

"Pourquoi name.c_str () est-il copié dans un pointeur CopyOfName? Cela signifie-t-il Que toutes les chaînes sont des pointeurs essentiels? Donc, comme test de chaîne = " Bonjour tout le monde "; __. où "H" est stocké? "

Comme Yu Hao l'a souligné dans son commentaire, il est important de comprendre la différence entre les chaînes de style C++ et les chaînes de style C. Dans le premier cas, vous avez affaire à un objet "opaque", alors que dans le dernier cas, vous avez affaire à un "tableau" de caractères. 

Avec les objets chaîne C++, vous pouvez utiliser la méthode c_str() pour obtenir un (pointeur sur un tableau) de caractères de style C. En C, un tableau est représenté à l'aide du pointeur vers le début du tableau, puis les références sont obtenues en fournissant un décalage (l'index dans le tableau) à partir de cette adresse de départ. Donc, la réponse à la dernière question de ce paquet est "oui", le pointeur sur la chaîne de style C est le pointeur sur le premier caractère, "H".

"Ensuite, pourquoi est-il dans l'instruction d'impression que CopyOfName n'est pas * CopyOfName? Les pointeurs contiennent des adresses mémoire?"

Parce que l'opérateur << est surchargé pour gérer les C-strings. L'implémentation de cette méthode "sait quoi faire avec" le pointeur.

3
Turix

Les pointeurs ne sont pas les mêmes que les tableaux. Les littéraux de chaîne sont immuables et lorsque vous avez un pointeur sur un littéral de chaîne, vous pouvez en examiner le contenu, mais leur modification constitue un comportement indéfini. Lorsque vous utilisez cette syntaxe:

char arr[] = "hi there";

Le littéral de chaîne est copié dans le tableau. Comme vous ne spécifiez pas de taille, le compilateur la déduit automatiquement. Le terminateur NUL est également ajouté automatiquement. Si vous spécifiez une taille, vous devez vous assurer que le tampon peut contenir le terminateur NUL. Donc:

char arr[5] = "hello";

est une erreur. Si vous utilisez la syntaxe d'initialisation d'accolade:

char arr[5] = { "h", "e", "l", "l", "o" };

C'est une erreur car il n'y a pas de terminateur NUL. Si vous utilisez strcpy, un terminateur NUL sera ajouté pour vous. 

std::string fournit deux méthodes pour renvoyer un pointeur sur son contenu sous-jacent: data et c_str. Avant C++ 11, la seule différence est que data n'inclut pas le terminateur NUL. En C++ 11, c'est maintenant le cas, leur comportement est donc identique. Comme le pointeur peut facilement être invalidé, il n’est pas prudent de manipuler ces pointeurs. char * ptr = str.c_str(); n'est également pas sûr car la durée de vie du tableau renvoyé par c_str meurt au point-virgule. Vous devez le copier dans un tampon.

3
user1508519

Vous posez les bonnes questions en tant qu'apprenant.

Réponses:

  1. En C++, string est un objet, la c_str() renvoie essentiellement un pointeur sur le premier caractère De la chaîne (Style C)
  2. Vous avez raison pour les chaînes en C, la variable pointe en fait sur le premier caractère de La chaîne
  3. C++ fait beaucoup de choses en fonction du type de variable. Lorsque vous passez un objet stringcout imprime la chaîne. En outre, C++ est suffisamment intelligent pour déterminer que *testing est illégal.
2
Sudhee

Pourquoi name.c_str () est-il copié dans un pointeur CopyOfName? 

"nom" est une chaîne STL. C'est un objet, qui est différent d'une c-string. Une chaîne de caractères est une collection de mémoire contenant des caractères et ayant une terminaison nulle. Donc, si vous utilisez des chaînes de la STL et que vous voulez les transformer en chaînes de caractères, vous utilisez .c_str () dessus pour obtenir la chaîne de caractères. 

CopyOfName contient assez de mémoire pour contenir le nom, puisqu'il a été alloué pour le contenir. 

cout a une tonne de choses que vous pouvez utiliser avec <<. On dirait que cela peut prendre les caractères * (qui sont des c-strings) ou les chaînes STL. Il ne semble pas que cela puisse prendre des pointeurs sur des chaînes STL. 

Je suis un peu confus lorsque vous avez introduit "testing", mais je pense que vous êtes en train de confondre entre c-strings (qui sont des caractères *) et des chaînes STL, qui sont des objets. Ne vous sentez pas mal ou n'abandonnez pas. Ce truc est délicat et prend un certain temps à obtenir.

Je recommanderais d'essayer de comprendre la différence entre les termes "c-string", "char *", "stl string" et peut-être "pointeur sur stl string".

2
Brian Stinar

En C, où les chaînes standard C++ n'existaient pas, char * était ce que l'on appelle "chaîne". Comme vous l'avez noté, il s'agit d'un tableau de caractères se terminant par un caractère NULL. À peu près n'importe quelle fonction de bibliothèque standard qui prend une chaîne de style C prendra un pointeur sur cette chaîne, pour deux raisons:

  1. Il est plus facile de penser à une chaîne de style C dans son ensemble plutôt que dans un ensemble de caractères, contrairement à d'autres tableaux, donc garder le pointeur garde cette idée
  2. Le moyen le plus simple de prendre un tableau en tant que paramètre de fonction consiste simplement à placer un pointeur sur le premier élément, en particulier dans le cas de chaînes C où elles peuvent uniquement être lues jusqu'au caractère NULL.
1
IllusiveBrian

Je pense que ce que vous faites, et d’autres, corrigez-moi si je me trompe, c’est que vous copiez votre chaîne dans un tableau de caractères dynamique. Donc non, vous ne le copiez pas dans un pointeur. La raison pour laquelle un pointeur est impliqué est que les tableaux dynamiques exigent que les pointeurs allouent leur mémoire correctement si j'ai raison.

1
jundl77