On voit très souvent, sur des forums communautaires, des membres qui conseillent aux autres d'utiliser
strncpy à la place de strcpy pour des raisons de sécurité. L'article que je vais vous présenter aujourd'hui tend à prétendre le contraire. À vous de vous forger votre avis à partir des arguments des deux camps.Un problème de débordement de tampon
Ce que reprochent ces membres à
strcpy, ce sont les problèmes de dépassements de tampons (overflow) qui sont engendrés par l'utilisation naïve de cette fonction de copie.char const *const s1 = "hello";
char s2[3];
strcpy(s2, s1);Dans le code ci-dessus, un dépassement mémoire est causé par la taille insuffisante de la chaîne s2.
La solution de facilité, strncpy
strncpy, de cette manière :char const *const s1 = "hello";
char s2[3];
strncpy(s2, s1, sizeof s2);Grâce au paramètre de taille, demandé par
strncpy, la fonction va pouvoir vérifier que la chaîne ne déborde pas.Un paramètre presque dérisoire...
Seulement, ce paramètre de taille est dérisoire. Tout simplement parce que vous devenez garder la trace de beaucoup de paramètres pour éviter le plantage de ces fonctions de manipulations des chaînes de caractère natives : le positionnement du caractère de fin de chaîne en est un exemple. Comme nous le verrons tout à l'heure,
strncpy ne gère même pas correctement cet élément...Par exemple, si votre chaîne de caractère est allouée statiquement, vous pouvez faire quelque chose comme le code ci-dessous pour utiliser
strcpy :if (strlen(s1) + 1 > sizeof s2) handle_errors();
else strcpy(s2, s1);Simplement, ça peut vous paraître idiot de réinventer la roue, puisque
strncpy semble faire la même chose Une fonction préjudiciable...
La gestion des erreurs
Le problème est que
strncpy ne gère pas les erreurs. Il est impossible de savoir si la fonction a échoué car la destination était trop petite.Le positionnement du caractère de fin de chaîne
De plus (et c'est inquiétant), aucun caractère de fin de chaîne n'est ajouté si on n'arrive pas à terme de la chaîne. Si on utilise d'autres fonctions de manipulations de chaînes, on se rend compte qu'elles prennent fortement en compte le positionnement du caractère de fin de chaîne. On a donc une forte vague d'erreurs qui s'enchaînent les unes les autres, et qui peuvent être difficiles à détecter.
On peut alors reprendre notre condition pour savoir si la taille n'est pas suffisante.
if (strlen(s1) + 1 > sizeof s2) handle_errors();
else strncpy(s2, s1, sizeof s2);Mais
strncpy revient alors à utiliser strcpy. Pour remédier à ce problème de zéro, on peut le placer manuellement après l'appel de strncpy.
strncpy(s2, s1, s2_size);
s2[s2_size - 1] = 0;Mais, là encore, on a un problème si
s2_size vaut zéro... Une condition de plus à ajouter, ce qui est préjudiciable dans des cas critiques...Les performances
strncpy est souvent contraignant pour les performances (pour que la fonction puisse tester la taille correctement, et parce qu'elle remplit les cases inutilisées par des zéros...).La sécurité
Prétendu symbole de sécurité, la fonction est pourtant sujette à l'exploitation du non-positionnement du caractère de fin de chaîne. Cela pourrait faire l'objet d'un autre article. En attendant, je vous renvoie à cette page.
Conclusion
Vous l'avez compris, strncpy possède également des défauts. Je ne défends pas particulièrement son utilisation, mais, du moins, il vous serait intéressant d'éviter la propagation de l'idée reçu selon laquelle strncpy est une fonction sûre et efficace.
Mais alors qu'utiliser ? Reste encore
snprintf (et oui, c'est du C99 !), qui a un système, certes simple, mais présent, de gestion des erreurs. Ce n'est pas très beau à utiliser, et c'est plus lent sur des petites chaînes. Mais ça me semble être une solution qui offre un bon compromis.