web-dev-qa-db-fra.com

Snprintf () se termine-t-il TOUJOURS nul?

Snprintf est-il toujours nul en terminant le tampon de destination?

En d'autres termes, cela suffit-il:

char dst[10];

snprintf(dst, sizeof (dst), "blah %s", somestr);

ou faut-il faire comme ça, si quelque temps 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 dit la norme et à ce que certaines libc populaires pourraient faire, ce qui n'est pas un comportement standard.

71
Prof. Falken

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

snprintf ... Écrit les résultats dans un tampon de chaîne de caractères. (...) se terminera par un caractère nul, sauf si buf_size vaut zéro.

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


Cependant, attention que la bibliothèque de Microsoft  n'a pas une fonction appelée snprintf mais à la place historiquement seulement avait une fonction appelée _snprintf (notez le trait de soulignement avant) qui ne s'ajoute pas un nul final. Voici la documentation (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 (sans inclure le caractère nul final). len et count sont en octets pour _snprintf, caractères larges pour _snwprintf.

  • Si len <count, alors len caractères sont stockés dans le tampon, un terminateur nul est ajouté et len ​​est renvoyé.

  • Si len = count, alors len caractères sont stockés dans le tampon, aucun terminateur nul n'est ajouté, et len ​​est renvoyé.

  • Si len> count, alors les caractères count sont stockés dans le tampon, aucun terminateur nul n'est ajouté et une valeur négative est retournée.

(...)

Visual Studio 2015 (VC14) a apparemment introduit la fonction snprintf conforme, mais l'héritage avec le soulignement de tête et le non comportement de terminaison nulle est toujours là:

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

Pour toutes les fonctions autre que snprintf, si len = count, len caractères sont stockés dans le tampon, aucun null-terminator n'est ajouté, ( ...)

64
Martin Ba

Selon la page de manuel snprintf (3).

Les fonctions snprintf() et vsnprintf() écrivent au plus size octets (y compris l'octet nul final ('\ 0')) dans str.

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

17
piotr

Selon la norme C, sauf si la taille de la mémoire tampon est 0, vsnprintf() et snprintf() null termine sa sortie.

La fonction snprintf() doit être équivalente à sprintf(), avec l'ajout de l'argument n qui indique la taille du tampon mentionné 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-1er 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 connaître la taille d'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'est pas prévu qu'il y ait une divergence entre Standard 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 n'est pas intentionnel. Ce volume de POSIX.1-2008 diffère de 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 renvoie -1 où la fonction standard renvoie la longueur requise). Il n'est pas entièrement clair que la version Microsoft null termine sa sortie dans des conditions d'erreur, contrairement à la version C standard.

Notez également les réponses à tilisez-vous les fonctions sûres du TR 24731? (voir MSDN pour la version Microsoft de la fonction vsprintf_s()) et Solution Mac pour les alternatives sûres aux fonctions de bibliothèque standard C dangereuses?

10
Jonathan Leffler

Certaines anciennes versions de SunOS ont fait des choses étranges avec snprintf et n'ont peut-être pas terminé la sortie NUL et ont des valeurs de retour qui ne correspondent pas à ce que tout le monde faisait, mais tout ce qui a été publié au cours des 10 dernières années a fait ce que C99 dit.

4
Art

L'ambiguïté part de 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 <stdio.h> int snprintf(char * restrict s, size_t n, const char * restrict format, ...);
Description
2 La fonction snprintf est équivalente à fprintf, sauf que la sortie est écrite dans un tableau (spécifié par l'argument s) plutôt que dans un flux. Si n vaut zéro, rien n'est écrit et s peut être un pointeur nul. Sinon, sortez les caractères au-delà de n-1st sont supprimés plutôt que 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 objets qui se chevauchent, le comportement n'est pas défini.
Renvoie
3 La fonction snprintf renvoie le nombre de caractères qui auraient été écrits si n avait été suffisamment grand, sans compter le caractère nul de fin, ou une valeur négative en cas d'erreur de codage eu lieu. Ainsi, la sortie terminée par un caractère nul a été complètement écrite si et seulement si la valeur retournée est non négative et inférieure à n.

D'une part la phrase

Sinon, affiche des caractères au-delà de n-1st sont supprimés plutôt que 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 vers un tableau de 3 caractères et) n est 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 cela, je crois, répond à la question initiale.
LA RÉPONSE:
Si la copie a lieu entre des objets qui se chevauchent, le comportement n'est pas défini.
Si n vaut 0 alors rien n'est écrit dans la sortie
sinon, si aucune erreur de codage n'est rencontrée, la sortie est TOUJOURS terminée par un caractère nul ( indépendamment du fait que la sortie rentre dans le tableau de sortie ou non ; sinon, certains caractères sont supprimés de sorte que le tableau de sortie ne soit jamais survolé),
sinon (si des erreurs de codage sont rencontrées), la sortie peut rester terminée par un caractère non nul .

Par contre
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 retournée est non négative et inférieure à n

donne l'ambiguïté (ou mon anglais n'est pas assez bon). Je peux interpréter cette phrase d'au moins deux façons:
1. La sortie est terminée par un caractère nul si et seulement si la valeur retournée est non négative et inférieure à n (ce qui signifie que si la valeur retournée n'est pas pas inférieure à n, c'est-à-dire la sortie (y compris le caractère nul final) ) ne tient pas dans le tableau, la sortie n'est pas terminée par un caractère nul ).
2. La sortie est complète (aucun caractère n'a été supprimé) si et seulement si la valeur retournée est non 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 a besoin d'un changement afin de lever toute ambiguïté (ce qui donne des raisons d'écrire une proposition dans la norme du langage C).
L'exemple d'une formulation non ambiguë, je crois, peut être tiré de http://en.cppreference.com/w/c/io/fprintf (voir 4)), merci à @ "Martin Ba" pour le lien.

Voir aussi la question " snprintf: Existe-t-il des propositions/plans standard C pour changer la description de cette fonction? ".

4
Robin Kuzmin