web-dev-qa-db-fra.com

Est-ce que C a un type de chaîne?

J'ai récemment commencé à programmer en C, en provenance de Java et de Python. Maintenant, dans mon livre, j'ai remarqué que pour créer un programme "Hello World", la syntaxe ressemblait à ceci:

char message[10]
strcpy(message, "Hello, world!")
printf("%s\n", message);

Maintenant, cet exemple utilise un tableau de caractères et je me suis demandé - qu'est-il arrivé aux chaînes? Pourquoi ne puis-je pas simplement en utiliser un? Peut-être qu'il y a une manière différente de faire ceci?

37
arielschon12

C n'a pas et n'a jamais eu de type de chaîne natif. Par convention, le langage utilise des tableaux de char terminés par un caractère nul, c'est-à-dire avec '\0'. Les fonctions et les macros dans les bibliothèques standard du langage prennent en charge les tableaux de caractères à terminaison nulle, par exemple, strlen itère sur un tableau de char jusqu'à ce qu'il rencontre un '\0' caractère et strcpy copie de la chaîne source jusqu’à ce qu’elle rencontre un '\0'.

L'utilisation de chaînes à terminaison nulle en C reflète le fait que C était censé n'être qu'un peu plus élevé que le langage Assembly. Les chaînes terminées à zéro étaient déjà directement prises en charge à cette époque en langage d'assemblage pour PDP-10 et PDP-11 .

Il est à noter que cette propriété de la chaîne C conduit à de nombreux bogues de saturation de la mémoire tampon, y compris de graves problèmes de sécurité. Par exemple, si vous oubliez de mettre fin à la valeur NULL d'une chaîne de caractères passée en tant qu'argument source à strcpy, la fonction continuera à copier les octets séquentiels de tout ce qui se trouve en mémoire après la fin de la chaîne source jusqu'à ce que cela se produise. rencontrer un 0, potentiellement écrasant les informations importantes qui suivent l’emplacement de la chaîne de destination en mémoire.

Dans votre exemple de code, le littéral de chaîne "Hello, world!" sera compilé dans un tableau long de 14 octets de char. Les 13 premiers octets contiendront les lettres, la virgule, l’espace et le point d’exclamation et le dernier octet contiendra le caractère de fin de chaîne nul '\0', ajouté automatiquement pour vous par le compilateur. Si vous deviez accéder au dernier élément du tableau, vous le trouverez égal à 0. Par exemple.:

const char foo[] = "Hello, world!";
assert(foo[12] == '!');
assert(foo[13] == '\0');

Cependant, dans votre exemple, message ne fait que 10 octets de long. strcpy va écrire les 14 octets, y compris le terminateur nul, en mémoire à partir de l'adresse de message. Les 10 premiers octets seront écrits dans la mémoire allouée sur la pile pour message et les quatre octets restants seront simplement écrits à la fin de la pile. La conséquence de l'écriture de ces quatre octets supplémentaires sur la pile est difficile à prévoir dans ce cas (dans cet exemple simple, cela ne ferait pas de mal), mais dans le code du monde réel, cela conduit généralement à des erreurs de violation des données ou d'accès à la mémoire.

64
dgvid

Il n'y a pas de type string dans C. Vous devez utiliser des tableaux de caractères.

En passant, votre code ne fonctionnera pas, car la taille du tableau devrait permettre à tout le tableau de s’adapter, plus un caractère de terminaison zéro supplémentaire.

14
Ivaylo Strandjev

À noter dans les langues que vous avez mentionnées:

Java:

String str = new String("Hello");

Python:

str = "Hello"

Java et Python ont le concept de "chaîne", C n'a pas le concept de "chaîne". C a des tableaux de caractères qui peuvent venir en "lecture seule" ou manipulable.

C:

char * str = "Hello";  // the string "Hello\0" is pointed to by the character pointer
                       // str. This "string" can not be modified (read only)

ou

char str[] = "Hello";  // the characters: 'H''e''l''l''o''\0' have been copied to the 
                       // array str. You can change them via: str[x] = 't'

Un tableau de caractères est une séquence de caractères contigus avec à la fin un caractère sentinelle unique (normalement un terminateur NULL '\0'). Notez que le caractère sentinelle est automatiquement ajouté par magie pour vous dans les cas ci-dessus.

7
Mike

En C, une chaîne est simplement un tableau de caractères, se terminant par un octet nul. Donc un char* est souvent prononcé "chaîne" lorsque vous lisez du code C.

7
Peter

C ne prend pas en charge un type de chaîne de première classe.

C++ a std :: string

3
wich

C n'a pas son propre type de données String comme Java.

Nous pouvons uniquement déclarer le type de données String en C en utilisant un tableau de caractères ou un pointeur de caractère. Par exemple:

 char message[10]; 
 or 
 char *message;

Mais vous devez au moins déclarer:

    char message[14]; 

copier "Bonjour le monde!" en variable de message.

  • 13: longueur du "Hello, world!"
  • 1: pour le caractère '\ 0' nul identifiant la fin de la chaîne
1
Babul Mirdha

Tout d'abord, vous n'avez pas besoin de faire tout cela. En particulier, le strcpy est redondant - vous n'avez pas besoin de copier une chaîne uniquement pour le printf -le. Votre message peut être défini avec cette chaîne en place.

Deuxièmement, vous n'avez pas laissé assez d'espace pour ce "Hello, World!" string (message doit comporter au moins 14 caractères, ce qui autorise le caractère supplémentaire pour le terminateur nul).

Sur le pourquoi, cependant, c'est l'histoire. En assembleur, il n'y a pas de chaînes, seulement des octets, des mots, etc. Pascal avait des chaînes, mais il y avait des problèmes avec le typage statique à cause de cela - string[20] était un type différent de string[40]. Même à ses débuts, certaines langues évitaient ce problème, mais cela entraînait des frais généraux d’allocation indirectionnels et dynamiques, qui étaient beaucoup plus un problème d’efficacité à l’époque.

C a simplement choisi d’éviter les frais généraux et de rester au plus bas niveau. Les chaînes sont des tableaux de caractères. Les tableaux sont très étroitement liés aux pointeurs qui pointent vers leur premier élément. Lorsque les types de tableau "se désintègrent" en types de pointeur, les informations relatives à la taille de la mémoire tampon sont perdues à partir du type statique, de sorte que vous n'obtenez pas les anciens problèmes de chaîne Pascal.

En C++, il y a le std::string classe qui évite beaucoup de ces problèmes - et a les frais généraux d’allocation dynamiques, mais de nos jours nous ne nous en soucions pas. Et dans tous les cas, std::string est une classe de bibliothèque - il y a une gestion de tableaux de caractères de style C dessous.

0
Steve314