Philosophie des chaînes de caractères
Comment représenter une chaîne de caractères comme “chouette” ?
Pleins représentations possibles
Pointeur vers une chaîne finissant par un caractère sentinelle
Comme vu, c’est le choix utilisé en C. C’est aussi le choix pour les chaînes bruts comme "chouette" en C++. C’est aussi le cas dans les langages Zig, Objective-C, assembleur, etc. Bien que Rust fait différemment, Rust le propose aussi pour pouvoir discuter avec les librairies C, cf. https://doc.rust-lang.org/std/ffi/struct.CStr.html.
"chouette"
Pointeur vers structure longueur + la chaîne à suivre
Il y a longtemps… c’était utilisé en Pascal. On mettait la longueur sur un octet puis les caractères.
Structure contenant un pointeur vers une chaîne + la longueur
C’est utilisé en Rust avec le type &str (référence sur str) qui est ce qu’on appelle un pointeur gras. Le pointeur est gras car il contient des informations en plus : ici le nombre d’octets. En Rust, accéder au i-ème caractère n’est pas en temps constant ! Pourquoi ? Parce que Rust gère UTF-8 et veut :
- garantir que la chaîne soit toujours UTF8-valide
- montrer que les fonctions couteuses ont une syntaxe horrible… donc pas de
s[3] = 'a'.
Le choix Python
Python est radical. Tout est pointeur en Python. Pointeur sur un objet compliqué… Et une chaîne de caractère est immutable, gère le UTF-8 et offre un accès direct lecture/écriture aux caractères en O(1). Chaque objet string ont des cases de la même taille, choisie en fonction du caractère qui prend le plus de place. Un emoji ? Tu paies le prix partout ! Voir https://github.com/python/cpython/blob/main/Include/cpython/unicodeobject.h.
…
Chaîne de caractères à la vecteur
En C++
En plus des char * hérité du C, on a le type string de #include <string> qui est un vecteur :
On peut tout faire : concaténer, modifier un caractère (mais aucune garantie que ce soit UTF-8 valide !) juste en faisant s[1] = 'b'.
En Rust
C’est similaire au C++. Mais le langage ne permet pas de modifier simplement un caractère, afin de garantir l’UTF-8-validité !
Discussions
Bref, Pour le bas niveau, on préfère un pointeur unique avec sentinelle :
- Ca prend moins de mémoire. Par exemple “abc” prend 4 octets VS 3 octets + une longueur codée sur 8 octets.
- C’était plus uniforme. Sur combien d’octets coder la longueur ?
- Les fonctions manipulent uniquement un pointeur.
Mais il y a des problèmes de :
- sécurité
- d’efficacité
Aller plus loin
- article philosophique sur les chaînes qui finissent par \0 : https://queue.acm.org/detail.cfm?id=2010365