web-dev-qa-db-fra.com

Différence entre les types string et char [] en C++

Je connais un peu le C et je regarde maintenant le C++ . Je suis habitué aux tableaux de caractères pour traiter les chaînes C, mais pendant que je regarde le code C++, je vois des exemples utilisant à la fois le type de chaîne et le caractère tableaux:

#include <iostream>
#include <string>
using namespace std;

int main () {
  string mystr;
  cout << "What's your name? ";
  getline (cin, mystr);
  cout << "Hello " << mystr << ".\n";
  cout << "What is your favorite team? ";
  getline (cin, mystr);
  cout << "I like " << mystr << " too!\n";
  return 0;
}

et

#include <iostream>
using namespace std;

int main () {
  char name[256], title[256];

  cout << "Enter your name: ";
  cin.getline (name,256);

  cout << "Enter your favourite movie: ";
  cin.getline (title,256);

  cout << name << "'s favourite movie is " << title;

  return 0;
}

(les deux exemples de http://www.cplusplus.com )

Je suppose que c’est une question largement posée et posée (évidente?), Mais ce serait bien si quelqu'un pouvait me dire quelle est exactement la différence entre ces deux façons de traiter les chaînes en C++ (performances, intégration des API, la façon dont chacune est meilleur, ...).

Je vous remercie.

108
ramosg

Un tableau de caractères est juste cela - un tableau de caractères:

  • Si alloué sur la pile (comme dans votre exemple), il occupera toujours par exemple. 256 octets, peu importe la longueur du texte qu'il contient
  • Si alloué sur le tas (en utilisant malloc () ou new char []), vous êtes responsable de la libération de la mémoire par la suite et vous aurez toujours le surcoût d'une allocation de tas. 
  • Si vous copiez un texte de plus de 256 caractères dans le tableau, il risque de se bloquer, de produire des messages d'assertion déplaisants ou de provoquer un comportement inexplicable (erroné) ailleurs dans votre programme.
  • Pour déterminer la longueur du texte, le tableau doit être analysé, caractère par caractère, pour un caractère\0.

Une chaîne est une classe qui contient un tableau de caractères, mais le gère automatiquement pour vous. La plupart des implémentations de chaînes ont un tableau intégré de 16 caractères (les chaînes courtes ne fragmentent donc pas le tas) et utilisent le tas pour les chaînes plus longues.

Vous pouvez accéder au tableau de caractères d'une chaîne comme ceci:

std::string myString = "Hello World";
const char *myStringChars = myString.c_str();

Les chaînes C++ peuvent contenir des caractères\0 incorporés, connaître leur longueur sans compter, sont plus rapides que les tableaux de caractères alloués par tas pour les textes courts et vous protéger des dépassements de mémoire tampon. De plus, ils sont plus lisibles et faciles à utiliser.

-

Cependant, les chaînes C++ ne sont pas (très) appropriées pour une utilisation à travers les frontières DLL, car cela demanderait à tout utilisateur de cette fonction DLL de s'assurer qu'il utilise exactement le même compilateur et la même implémentation d'exécution C++, de peur il risque que sa classe de cordes se comporte différemment.

Normalement, une classe de chaîne libère également sa mémoire heap sur le heap appelant. Elle ne pourra donc libérer de la mémoire que si vous utilisez une version partagée (.dll ou .so) du runtime.

En bref: utilisez des chaînes C++ dans toutes vos fonctions et méthodes internes. Si vous écrivez un fichier .dll ou .so, utilisez des chaînes C dans vos fonctions publiques (dll/so-exposure).

159
Cygon

Arkaitz a raison de dire que string est un type géré. Ce que cela signifie pour vous est que vous ne devez jamais vous soucier de la longueur de la chaîne, ni de la libération ou de la réaffectation de la mémoire de la chaîne.

D'autre part, la notation char[] dans le cas ci-dessus a limité le tampon de caractères à exactement 256 caractères. Si vous avez essayé d'écrire plus de 256 caractères dans ce tampon, vous écraserez au mieux l'autre mémoire que votre programme "possède". Dans le pire des cas, vous tenterez d'écraser la mémoire que vous ne possédez pas et votre système d'exploitation tuera votre programme sur-le-champ.

Ligne de fond? Les chaînes sont beaucoup plus conviviales pour les programmeurs, les caractères [] sont beaucoup plus efficaces pour l'ordinateur.

10
Mark Rushakoff

Eh bien, le type de chaîne est une classe entièrement gérée pour les chaînes de caractères, tandis que char [] est toujours ce qu’il était en C, un tableau d’octets représentant une chaîne de caractères pour vous.

En termes d'API et de bibliothèque standard, tout est implémenté en termes de chaînes et non de char [], mais il y a encore beaucoup de fonctions de la libc qui reçoivent char [], vous devrez donc peut-être l'utiliser pour ceux-là, à part cela, utilisez toujours std :: string.

En termes d’efficacité, bien sûr, un tampon brut de mémoire non gérée sera presque toujours plus rapide pour beaucoup de choses, mais prenez en compte la comparaison de chaînes, par exemple, std :: string a toujours la taille pour le vérifier en premier, alors qu'avec char [], besoin de comparer caractère par caractère.

6
Arkaitz Jimenez

Personnellement, je ne vois aucune raison pour laquelle on souhaiterait utiliser char * ou char [], à l'exception de la compatibilité avec l'ancien code. std :: string n'est pas plus lent que d'utiliser une c-string, sauf qu'il gérera la réallocation pour vous. Vous pouvez définir sa taille lors de la création et éviter ainsi une réallocation si vous le souhaitez. Son opérateur d'indexation ([]) fournit un accès constant dans le temps (et, dans tous les sens du mot, exactement la même chose que d'utiliser un indexeur c-string). L'utilisation de la méthode at vous donne également une limite de sécurité contrôlée, quelque chose que vous n'obtenez pas avec les c-strings, à moins que vous ne l'écriviez. Votre compilateur optimisera le plus souvent l'utilisation de l'indexeur en mode publication. Il est facile de jouer avec les c-strings; des choses telles que delete vs delete [], la sécurité des exceptions, même la réaffectation d’une chaîne de caractères.

Et lorsque vous devez faire face à des concepts avancés comme avoir des chaînes COW et non COW pour MT, etc., vous aurez besoin de std :: string.

Si les copies vous inquiètent, tant que vous utilisez des références et des références const chaque fois que vous le pouvez, vous ne subirez aucune surcharge du fait des copies, et c'est la même chose que si vous utilisiez la chaîne de caractères c.

5
Abhay

Les chaînes ont des fonctions d'assistance et gèrent les tableaux de caractères automatiquement. Vous pouvez concaténer des chaînes. Pour un tableau de caractères, vous devez le copier dans un nouveau tableau. Les chaînes peuvent changer de longueur lors de l'exécution. Un tableau de caractères est plus difficile à gérer qu'une chaîne et certaines fonctions peuvent n'accepter qu'une chaîne en tant qu'entrée, ce qui nécessite de convertir le tableau en chaîne. Il est préférable d'utiliser des chaînes, elles ont été conçues pour que vous n'ayez pas à utiliser de tableaux. Si les tableaux étaient objectivement meilleurs, nous n'aurions pas de chaînes.

1
theo2003

Une des différences est la terminaison nulle (\ 0).

En C et C++, char * ou char [] prend un pointeur sur un caractère unique en tant que paramètre et suit la mémoire jusqu'à atteindre une valeur de mémoire égale à 0 (souvent appelée terminateur nul).

Les chaînes C++ peuvent contenir des caractères\0 incorporés, connaître leur longueur sans compter. 

#include<stdio.h>
#include<string.h>
#include<iostream>

using namespace std;

void NullTerminatedString(string str){
   int NUll_term = 3;
   str[NUll_term] = '\0';       // specific character is kept as NULL in string
   cout << str << endl <<endl <<endl;
}

void NullTerminatedChar(char *str){
   int NUll_term = 3;
   str[NUll_term] = 0;     // from specific, all the character are removed 
   cout << str << endl;
}

int main(){
  string str = "Feels Happy";
  printf("string = %s\n", str.c_str());
  printf("strlen = %d\n", strlen(str.c_str()));  
  printf("size = %d\n", str.size());  
  printf("sizeof = %d\n", sizeof(str)); // sizeof std::string class  and compiler dependent
  NullTerminatedString(str);


  char str1[12] = "Feels Happy";
  printf("char[] = %s\n", str1);
  printf("strlen = %d\n", strlen(str1));
  printf("sizeof = %d\n", sizeof(str1));    // sizeof char array
  NullTerminatedChar(str1);
  return 0;
}

Sortie:

strlen = 11
size = 11
sizeof = 32  
Fee s Happy


strlen = 11
sizeof = 12
Fee
0
Eswaran Pandi

Pensez à (char *) comme string.begin (). La différence essentielle est que (char *) est un itérateur et que std :: string est un conteneur. Si vous vous en tenez aux chaînes de base, un (char *) vous donnera ce que std :: string :: iterator fait. Vous pouvez utiliser (char *) lorsque vous souhaitez bénéficier d'un itérateur et de la compatibilité avec C, mais c'est l'exception et non la règle. Comme toujours, faites attention à l’invalidation de l’itérateur. Quand les gens disent que (char *) n'est pas sûr, c'est ce qu'ils veulent dire. C'est aussi sûr que n'importe quel autre itérateur C++.

0
Samuel Danielson