Snprintf () ALWAYS est-il toujours terminé?

Est-ce que snprintf met toujours à zéro le tampon de destination?

En d’autres termes, est-ce suffisant?

char dst[10]; snprintf(dst, sizeof (dst), "blah %s", somestr); 

ou faut-il faire comme ça, si quelque chose est assez long?

 char dst[10]; somestr[sizeof (dst) - 1] = '\0'; snprintf(dst, sizeof (dst) - 1, "blah %s", somestr); 

Je m’intéresse à la fois à ce que la norme dit et à ce que certaines libc populaires pourraient faire, ce qui n’est pas un comportement standard.

Comme les autres réponses établissent: Il devrait :

snprintf … Ecrit les résultats dans un tampon de chaîne de caractères. (…) se terminera par un caractère nul, sauf si buf_size est égal à zéro.

Donc tout ce que vous avez à faire est de ne pas lui passer un tampon de taille nulle, car (évidemment) il ne peut pas écrire un zéro à “nulle part”.


Cependant, méfiezvous que la bibliothèque de Microsoft ne possède pas de fonction appelée snprintf mais, au lieu de cela, n’avait historiquement qu’une fonction appelée _snprintf (notez le trait de soulignement) qui n’ajoute pas de valeur NULL. Voici les documents (VS 2012, ~~ VS 2013):

http://msdn.microsoft.com/en-us/library/2ts7cx93%28v=vs.110%29.aspx

Valeur de retour

Soit len ​​la longueur de la chaîne de données formatée (n’incluant pas la valeur NULL finale). len et count sont en octets pour _snprintf, caractères larges pour _snwprintf.

  • Si len

  • Si len = count, les caractères len sont stockés dans le tampon, aucun null-terminateur n’est ajouté et len ​​est renvoyé.

  • Si len> count, alors le nombre de caractères est stocké dans le tampon, aucun null-terminateur n’est ajouté et une valeur négative est renvoyée.

(…)

Visual Studio 2015 (VC14) a apparemment introduit la fonction snprintf conforme, mais l’ancienne avec le trait de soulignement principal et le comportement de terminaison non null existe toujours:

La fonction snprintf tronque la sortie lorsque len est supérieur ou égal à count, en plaçant un terminateur null au buffer[count-1] . (…)

Pour toutes les fonctions autres que snprintf , si len = count, les caractères len sont stockés dans le tampon, aucun null-terminateur n’est ajouté , (…)

Selon la page de manuel de snprintf (3).

Les fonctions snprintf() et vsnprintf() écrivent sur la plupart des octets de size (y compris le dernier octet nul (‘\ 0’)) à str .

Donc, oui, pas besoin de terminer si la taille> = 1.

Selon la norme C, à moins que la taille de la mémoire tampon ne soit vsnprintf() 0, vsnprintf() et snprintf() null terminent leur sortie.

La fonction snprintf() doit être équivalente à sprintf() , avec l’ajout de l’argument n qui indique la taille de la mémoire tampon mentionnée par s. Si n est zéro, rien ne sera écrit et s peut être un pointeur nul. Sinon, les octets de sortie au-delà du n-1st doivent être ignorés au lieu d’être écrits dans le tableau, et un octet nul est écrit à la fin des octets réellement écrits dans le tableau.

Donc, si vous avez besoin de savoir quelle taille un tampon à allouer, utilisez une taille de zéro, et vous pouvez ensuite utiliser un pointeur nul comme destination. Notez que j’ai lié aux pages POSIX, mais celles-ci disent explicitement qu’il n’y a pas de divergence entre la norme C et POSIX où elles couvrent le même terrain:

La fonctionnalité décrite sur cette page de référence est alignée sur la norme ISO C. Tout conflit entre les exigences décrites ici et la norme ISO C est involontaire. Ce volume de POSIX.1-2008 est conforme à la norme ISO C.

Méfiez-vous de la version Microsoft de vsnprintf() . Il se comporte définitivement différemment de la version C standard lorsqu’il n’y a pas assez d’espace dans le tampon (il retourne -1 où la fonction standard renvoie la longueur requirejse). Il n’est pas tout à fait clair que la version null de Microsoft termine sa sortie dans des conditions d’erreur, alors que la version standard C le fait.

Notez également les réponses à la question Utilisez-vous les fonctions de sécurité TR 24731? (voir MSDN pour la version Microsoft de vsprintf_s() ) et la solution Mac pour les alternatives sûres aux fonctions de bibliothèque standard C peu sûres?

Certaines versions plus anciennes de SunOS faisaient des choses bizarres avec snprintf et pouvaient ne pas avoir terminé la sortie avec NUL et avaient des valeurs de retour qui ne correspondaient pas à celles de tout le monde, mais tout ce qui a été publié ces 10 dernières années dit.

L’ambiguïté commence par la norme C elle-même. C99 et C11 ont une description identique de la fonction snprintf . Voici la description de C99:

7.19.6.5 La fonction snprintf
Synopsis
1 #include int snprintf(char * ressortingct s, size_t n, const char * ressortingct format, ...);
La description
2 La fonction snprintf est équivalente à fprintf , sauf que la sortie est écrite dans un tableau (spécifié par un argument s ) plutôt que dans un stream. Si n est zéro, rien n’est écrit et s peut être un pointeur nul. Sinon, les caractères de sortie au-delà du n-1 st sont ignorés au lieu d’être écrits dans le tableau, et un caractère nul est écrit à la fin des caractères réellement écrits dans le tableau. Si la copie a lieu entre des objects qui se chevauchent, le comportement est indéfini.
Résultats
3 La fonction snprintf renvoie le nombre de caractères qui auraient été écrits si elle était suffisamment grande, sans compter le caractère nul snprintf ou une valeur négative en cas d’erreur de codage. Ainsi, la sortie terminée par un caractère nul a été complètement écrite si et seulement si la valeur renvoyée est non négative et inférieure à n .

D’une part la phrase

Sinon, les caractères de sortie au-delà du n-1 st sont supprimés au lieu d’être écrits dans le tableau, et un caractère nul est écrit à la fin des caractères réellement écrits dans le tableau.

dit ça
si (le s pointe sur un tableau de 3 caractères) et que n vaut 3, alors 2 caractères seront écrits et les caractères au-delà du 2 ème seront supprimés ; alors le caractère nul est écrit après ces 2 (et le caractère nul sera le 3ème caractère écrit) .

Et je crois que cela répond à la question initiale.
LA RÉPONSE:
Si la copie a lieu entre des objects qui se chevauchent, le comportement est indéfini.
Si n est 0 alors rien n’est écrit dans la sortie
sinon, si aucune erreur de codage n’a été détectée, la sortie est TOUJOURS terminée par un caractère nul ( que la sortie rentre ou non dans le tableau de sortie ; sinon, certains caractères sont ignorés de sorte que le tableau de sortie ne soit jamais survolé),
sinon (si des erreurs de codage sont rencontrées), la sortie peut restr non-NULL .

D’autre part
La dernière phrase

Ainsi, la sortie terminée par un caractère nul a été complètement écrite si et seulement si la valeur renvoyée est non négative et inférieure à n

donne de l’ ambiguïté (ou mon anglais n’est pas assez bon). Je peux interpréter cette phrase au moins de deux manières:
1. La sortie est terminée par un caractère nul si et seulement si la valeur renvoyée est non négative et inférieure à n (ce qui signifie que si la valeur renvoyée n’est pas inférieure à n , c’est-à-dire que la array, alors la sortie n’est pas terminée par un null ).
2. La sortie est complète (aucun caractère n’a été supprimé) si et seulement si la valeur renvoyée est négative et inférieure à n .


Je crois que l’interprétation 1 ci-dessus contredit LA RÉPONSE, provoque des malentendus et de longues discussions. C’est pourquoi la dernière phrase décrivant la fonction snprintf nécessite un changement afin de supprimer toute ambiguïté (ce qui justifie l’écriture d’une proposition dans le standard du langage C).
Je crois que l’exemple de libellé non ambigu peut être extrait de http://en.cppreference.com/w/c/io/fprintf (voir 4) ), grâce à @ “Martin Ba” pour le lien.

Voir aussi la question ” snprintf: existe-t-il des propositions / plans standards C pour modifier la description de cette fonction? “.