Le langage de programmation C dispose d'un ensemble de fonctions implémentant des opérations sur des chaînes (chaînes de caractères et chaînes d'octets) dans sa bibliothèque standard . Diverses opérations, telles que la copie, la concaténation , la tokenisation et la recherche sont prises en charge. Pour les chaînes de caractères, la bibliothèque standard utilise la convention selon laquelle les chaînes sont terminées par un caractère nul : une chaîne de n caractères est représentée comme un tableau de n + 1 éléments, dont le dernier est un « caractère NUL » de valeur numérique 0.
Le seul support pour les chaînes dans le langage de programmation proprement dit est que le compilateur traduit les constantes de chaîne entre guillemets en chaînes terminées par un caractère nul.
Définitions
Une chaîne est définie comme une séquence contiguë d' unités de code terminée par la première unité de code zéro (souvent appelée unité de code NUL ). Cela signifie qu'une chaîne ne peut pas contenir l'unité de code zéro, car la première unité vue marque la fin de la chaîne. La longueur d'une chaîne est le nombre d'unités de code avant l'unité de code zéro. La mémoire occupée par une chaîne est toujours une unité de code de plus que la longueur, car de l'espace est nécessaire pour stocker le terminateur zéro.
En général, le terme chaîne désigne une chaîne dont l'unité de code est de type char, soit exactement 8 bits sur toutes les machines modernes. C90 définit des chaînes larges qui utilisent une unité de code de type wchar_t, soit 16 ou 32 bits sur les machines modernes. Cela était prévu pour Unicode, mais il est de plus en plus courant d'utiliser UTF-8 dans les chaînes normales pour Unicode à la place.
Les chaînes sont transmises aux fonctions en transmettant un pointeur vers la première unité de code. Étant donné que char *et wchar_t *sont de types différents, les fonctions qui traitent des chaînes larges sont différentes de celles qui traitent des chaînes normales et ont des noms différents.
Les littéraux de chaîne ( "text"dans le code source C) sont convertis en tableaux pendant la compilation. Le résultat est un tableau d'unités de code contenant tous les caractères plus une unité de code zéro à la fin. Dans C90, cela L"text"produit une chaîne large. Un littéral de chaîne peut contenir l'unité de code zéro (une façon est de le mettre \0dans la source), mais cela entraînera la fin de la chaîne à ce point. Le reste du littéral sera placé en mémoire (avec une autre unité de code zéro ajoutée à la fin) mais il est impossible de savoir que ces unités de code ont été traduites à partir du littéral de chaîne, donc un tel code source n'est pas un littéral de chaîne.
Codages de caractères
Chaque chaîne se termine à la première occurrence de l'unité de code zéro du type approprié ( charou wchar_t). Par conséquent, une chaîne d'octets ( char*) peut contenir des caractères non NUL en ASCII ou toute extension ASCII , mais pas de caractères dans des codages tels que UTF-16 (même si une unité de code 16 bits peut être différente de zéro, son octet haut ou bas peut être zéro). Les codages qui peuvent être stockés dans des chaînes larges sont définis par la largeur de wchar_t. Dans la plupart des implémentations, wchar_test d'au moins 16 bits, et donc tous les codages 16 bits, tels que UCS-2 , peuvent être stockés. Si wchar_test de 32 bits, alors les codages 32 bits, tels que UTF-32 , peuvent être stockés. (La norme exige un « type qui contient n'importe quel caractère large », ce qui n'est plus vrai sous Windows depuis le passage de l'UCS-2 à l'UTF-16. Cela a été reconnu comme un défaut dans la norme et corrigé dans C++.) C++11 et C11 ajoutent deux types avec des largeurs explicites char16_tet char32_t.
Les codages à largeur variable peuvent être utilisés à la fois dans les chaînes d'octets et dans les chaînes larges. La longueur et les décalages des chaînes sont mesurés en octets ou wchar_t, et non en « caractères », ce qui peut prêter à confusion pour les programmeurs débutants. UTF-8 et Shift JIS sont souvent utilisés dans les chaînes d'octets C, tandis que UTF-16 est souvent utilisé dans les chaînes larges C lorsqu'il wchar_test de 16 bits. La troncature de chaînes avec des caractères à largeur variable à l'aide de fonctions telles que strncpypeut produire des séquences non valides à la fin de la chaîne. Cela peut être dangereux si les parties tronquées sont interprétées par un code qui suppose que l'entrée est valide.
La prise en charge des littéraux Unicode tels que (UTF-8) ou (UTF-16 ou UTF-32, dépend de ) est définie par l'implémentation, et peut nécessiter que le code source soit dans le même codage, en particulier lorsque les compilateurs peuvent simplement copier tout ce qui se trouve entre les guillemets. Certains compilateurs ou éditeurs nécessiteront la saisie de tous les caractères non-ASCII sous forme de séquences pour chaque octet d'UTF-8 et/ou pour chaque mot d'UTF-16. Depuis C11 (et C++11), un nouveau préfixe littéral est disponible qui garantit UTF-8 pour un littéral de chaîne d'octets, comme dans . Depuis C++20 et C23 , un type a été ajouté qui est destiné à stocker des caractères UTF-8 et les types de caractères et de chaînes littéraux préfixés u8 ont été modifiés respectivement en et . charfoo[512]="φωωβαρ";wchar_tfoo[512]=L"φωωβαρ";wchar_tchar\xNN\uNNNNu8charfoo[512]=u8"φωωβαρ";char8_tchar8_tchar8_t[]
Caractéristiques
Terminologie
Dans la documentation historique, le terme « caractère » était souvent utilisé à la place de « octet » pour les chaînes C, ce qui conduit de nombreuses personnes à croire que ces fonctions ne fonctionnent pas pour UTF-8 . En fait, toutes les longueurs sont définies comme étant en octets et cela est vrai dans toutes les implémentations, et ces fonctions fonctionnent aussi bien avec UTF-8 qu'avec les codages sur un seul octet. La documentation BSD a été corrigée pour clarifier cela, mais la documentation POSIX, Linux et Windows utilise toujours « caractère » dans de nombreux endroits où « octet » ou « wchar_t » est le terme correct.
Les fonctions de gestion des tampons de mémoire peuvent traiter des séquences d'octets qui incluent des octets nuls dans les données. Les noms de ces fonctions commencent généralement par mem, à l'opposé du strpréfixe.
En-têtes
La plupart des fonctions qui opèrent sur des chaînes C sont déclarées dans l' string.hen-tête ( cstringen C++), tandis que les fonctions qui opèrent sur des chaînes C sont déclarées dans l' wchar.hen-tête ( cwcharen C++). Ces en-têtes contiennent également des déclarations de fonctions utilisées pour gérer les tampons mémoire ; le nom est donc quelque peu impropre.
Les fonctions déclarées dans string.hsont extrêmement populaires car, en tant que partie de la bibliothèque standard C , elles sont garanties de fonctionner sur toute plate-forme prenant en charge C. Cependant, certains problèmes de sécurité existent avec ces fonctions, tels que des débordements de tampon potentiels lorsqu'elles ne sont pas utilisées avec précaution et correctement, ce qui incite les programmeurs à préférer des variantes plus sûres et éventuellement moins portables, parmi lesquelles certaines sont répertoriées ci-dessous. Certaines de ces fonctions violent également la cohérence en acceptant un constpointeur de chaîne et en renvoyant un constpointeur non- dans la chaîne. Pour corriger cela, certaines ont été séparées en deux fonctions surchargées dans la version C++ de la bibliothèque standard.
Constantes et types
Fonctions
Fonctions multi-octets
Ces fonctions nécessitent toutes unmbstate_tobjet, à l'origine dans la mémoire statique (ce qui rend les fonctions non thread-safe) et dans les ajouts ultérieurs, l'appelant doit maintenir. Cela était à l'origine destiné à suivre les états de décalage dans leMocodages, mais les codages modernes tels que UTF-8 n'en ont pas besoin. Cependant, ces fonctions ont été conçues sur l'hypothèse quetoilettesl'encodage n'est pas un encodage à largeur variable et est donc conçu pour traiter exactement unwchar_tà la fois, en le passant par valeur plutôt qu'en utilisant un pointeur de chaîne. Comme UTF-16 est un codage à largeur variable, lembstate_ta été réutilisé pour garder une trace des paires de substitution dans le codage large, bien que l'appelant doive toujours détecter et appelermbtowcdeux fois pour un seul caractère. Les ajouts ultérieurs à la norme admettent que la seule conversion qui intéresse les programmeurs est celle entre UTF-8 et UTF-16 et la fournissent directement.
Conversions numériques
La bibliothèque standard C contient plusieurs fonctions pour les conversions numériques. Les fonctions qui traitent les chaînes d'octets sont définies dans l' stdlib.hen-tête ( cstdlibheader en C++). Les fonctions qui traitent les chaînes larges sont définies dans l' wchar.hen-tête ( cwcharheader en C++).
Les fonctions strchr, bsearch, strpbrk, strrchr, strstr, memchret leurs équivalents larges ne sont pas const-correctes , car elles acceptent un constpointeur de chaîne et renvoient un non- constpointeur dans la chaîne. Cela a été corrigé dans C23 .
De plus, depuis l'amendement normatif 1 (C95), atoxxles fonctions sont considérées comme subsumées par strtoxxxdes fonctions, raison pour laquelle ni la C95 ni aucune norme ultérieure ne fournit de versions à caractères larges de ces fonctions. L'argument contre atoxxest qu'elles ne font pas la différence entre une erreur et un 0.
Extensions populaires
Remplaçants
Malgré le besoin bien établi de remplacer strcat et strcpy par des fonctions qui ne permettent pas les dépassements de tampon, aucune norme acceptée n'a été établie. Cela est en partie dû à la croyance erronée de nombreux programmeurs C selon laquelle strncatet strncpyont le comportement souhaité ; cependant, aucune des deux fonctions n'a été conçue pour cela (elles étaient destinées à manipuler des tampons de chaîne de taille fixe avec remplissage nul, un format de données moins couramment utilisé dans les logiciels modernes), et le comportement et les arguments ne sont pas intuitifs et souvent écrits de manière incorrecte, même par des programmeurs experts.
Les fonctions de remplacement les plus populaires sont les fonctions strlcat et strlcpy , apparues dans OpenBSD 2.4 en décembre 1998. Ces fonctions écrivent toujours un NUL dans le tampon de destination, tronquant le résultat si nécessaire, et renvoient la taille du tampon qui serait nécessaire, ce qui permet de détecter la troncature et fournit une taille pour créer un nouveau tampon qui ne sera pas tronqué. Pendant longtemps, elles n'ont pas été incluses dans la bibliothèque GNU C (utilisée par les logiciels sous Linux), au motif qu'elles seraient prétendument inefficaces, encourageant l'utilisation de chaînes C (au lieu d'une forme alternative de chaîne supérieure), et cachant d'autres erreurs potentielles. Même si la glibc n'avait pas ajouté de support, strlcat et strlcpy ont été implémentés dans un certain nombre d'autres bibliothèques C, notamment celles pour OpenBSD, FreeBSD , NetBSD , Solaris , OS X et QNX , ainsi que dans d'autres bibliothèques C pour Linux, telles que libbsd, introduite en 2008, et musl , introduite en 2011, et le code source a été ajouté directement à d'autres projets tels que SDL , GLib , ffmpeg , rsync , et même en interne dans le noyau Linux . Cela a changé en 2024, la FAQ de la glibc note qu'à partir de la glibc 2.38, le code a été validé et donc ajouté. Ces fonctions ont été normalisées dans le cadre de POSIX.1-2024, l'Austin Group Defect Tracker ID 986 a suivi certaines discussions sur de tels plans pour POSIX.
Parfois, memcpy ou memmove sont utilisés, car ils peuvent être plus efficaces car strcpyils ne vérifient pas à plusieurs reprises la présence de NUL (ce qui est moins vrai sur les processeurs modernes). Comme ils nécessitent une longueur de tampon comme paramètre, un réglage correct de ce paramètre peut éviter les débordements de tampon.
Dans le cadre de son cycle de vie de développement de sécurité de 2004 , Microsoft a introduit une famille de fonctions « sécurisées » comprenant strcpy_set strcat_s(ainsi que de nombreuses autres). Ces fonctions ont été normalisées avec quelques modifications mineures dans le cadre du C11 facultatif (annexe K) proposé par ISO/IEC WDTR 24731. Ces fonctions effectuent diverses vérifications, notamment si la chaîne est trop longue pour tenir dans le tampon. Si les vérifications échouent, une fonction « runtime-constraint handler » spécifiée par l'utilisateur est appelée, ce qui interrompt généralement le programme. Ces fonctions ont suscité de nombreuses critiques car elles n'étaient initialement implémentées que sur Windows et, au même moment, des messages d'avertissement ont commencé à être produits par Microsoft Visual C++ suggérant l'utilisation de ces fonctions au lieu des fonctions standard. Certains ont émis l'hypothèse qu'il s'agissait d'une tentative de Microsoft pour enfermer les développeurs dans sa plateforme. L'expérience avec ces fonctions a montré des problèmes importants avec leur adoption et des erreurs d'utilisation, donc la suppression de l'annexe K est proposée pour la prochaine révision de la norme C. L'utilisation de memset_sa été suggérée comme un moyen d'éviter les optimisations indésirables du compilateur.