Détermination de la taille du tampon sprintf – quelle est la norme?

Lors de la conversion d’un int comme ça:

char a[256]; sprintf(a, "%d", 132); 

Quelle est la meilleure façon de déterminer la taille d’ un devrait être? Je suppose que le réglage manuel est correct (comme je l’ai vu partout), mais quelle devrait être sa taille? Quelle est la plus grande valeur int possible sur un système 32 bits, et y a-t-il une manière délicate de le déterminer à la volée?

Le nombre maximum possible de bits dans un int est CHAR_BIT * sizeof(int) , et un chiffre décimal vaut “au moins 3 bits”, donc une limite supérieure de l’espace requirejs pour un int arbitraire est (CHAR_BIT * sizeof(int) / 3) + 3 . Ce +3 est un pour le fait que nous avons arrondi à la division, un pour le signe, un pour le terminateur nul.

Si par “sur un système 32 bits” vous voulez dire que vous savez que int est de 32 bits, alors vous avez besoin de 12 octets. 10 pour les chiffres, un pour le signe, un pour le terminateur nul.

Dans votre cas spécifique, où le int à convertir est 132 , vous avez besoin de 4 octets. Badum, tish.

Lorsque des tampons de taille fixe peuvent être utilisés avec une limite raisonnable, ils constituent l’option la plus simple. Je ne soumets pas si humblement que la limite ci-dessus est raisonnable (13 octets au lieu de 12 pour un int 32 bits et 23 octets au lieu de 21 pour un int 64 bits). Mais pour les cas difficiles, en C99, vous pouvez simplement appeler snprintf pour obtenir la taille, puis malloc . C’est exagéré pour un cas aussi simple que cela.

Certains prétendent que cette approche est exagérée et que pour convertir les ints en chaînes, je serais plus enclin à être d’accord. Mais quand une limite raisonnable pour la taille de la chaîne ne peut pas être trouvée, j’ai vu cette approche utilisée et l’ai utilisée moi-même.

 int size = snprintf(NULL, 0, "%d", 132); char * a = malloc(size + 1); sprintf(a, "%d", 132); 

Je vais décomposer ce qui se passe ici.

  1. Sur la première ligne, nous voulons déterminer combien de caractères nous avons besoin. Les 2 premiers arguments de snprintf indiquent que je veux écrire 0 caractères du résultat à NULL . Lorsque nous faisons cela, snprintf n’écrira aucun caractère, il renverra simplement le nombre de caractères qui auraient été écrits. C’est ce que nous voulions.
  2. Sur la deuxième ligne, nous allouons dynamicment de la mémoire à un pointeur de caractère. Assurez-vous et ajoutez 1 à la taille requirejse (pour le caractère de fin \0 final).
  3. Maintenant que la mémoire allouée au pointeur est suffisante, vous pouvez utiliser sprintf en toute sécurité pour écrire le nombre entier dans le pointeur de caractère.

Bien sûr, vous pouvez le rendre plus concis si vous le souhaitez.

 char * a = malloc(snprintf(NULL, 0, "%d", 132) + 1); sprintf(a, "%d", 132); 

À moins que ce ne soit un programme “rapide et sale”, vous voulez toujours vous assurer de libérer la mémoire que vous avez appelée avec malloc . C’est là que l’approche dynamic se complique avec C. Cependant, à mon humble avis, si vous ne voulez pas atsortingbuer d’énormes pointeurs lorsque la plupart du temps vous n’en utiliserez qu’une très petite partie, alors je ne pense pas c’est une mauvaise approche.

Il est possible de faire fonctionner la solution de Daniel Standage pour un nombre quelconque d’arguments en utilisant vsnprintf qui est en C ++ 11 / C99.

 int bufferSize(const char* format, ...) { va_list args; va_start(args, format); int result = vsnprintf(NULL, 0, format, args); va_end(args); return result + 1; // safe byte for \0 } 

Comme spécifié dans la norme c99 , section 7.19.6.12:

La fonction vsnprintf renvoie le nombre de caractères qui auraient été écrits si n était suffisamment grand, sans compter le caractère nul final, ou une valeur négative si
erreur de codage s’est produite.

Je vois que cette conversation a quelques années, mais je l’ai trouvée en essayant de trouver une réponse pour MS VC ++ où snprintf ne peut pas être utilisé pour trouver la taille. Je posterai la réponse que j’ai finalement trouvée au cas où cela aiderait quelqu’un d’autre:

VC ++ a la fonction _scprintf spécifiquement pour trouver le nombre de caractères nécessaires.

Si vous imprimez un entier simple, et rien d’autre, il existe un moyen beaucoup plus simple de déterminer la taille du tampon de sortie. Au moins plus simple, le code est un peu obtus:

 char *text; text = malloc(val ? (int)log10((double)abs(val)) + (val < 0) + 2 : 2); 

log10 (valeur) renvoie le nombre de chiffres (moins un) requirejs pour stocker une valeur positive différente de zéro dans la base 10. Il passe un peu hors des rails pour les nombres inférieurs à un, donc nous spécifions abs () et codons une logique spéciale pour zéro (l'opérateur ternaire, test? truecase: falsecase). Ajoutez-en un pour l'espace pour stocker le signe d'un nombre négatif (val <0), un pour compenser la différence avec log10, et un autre pour le terminateur nul (pour un total général de 2), et vous venez de calculer la quantité exacte d'espace de stockage requise pour un nombre donné, sans appeler snprintf () ou ses équivalents deux fois pour terminer le travail. De plus, il utilise généralement moins de mémoire que le système INT_MAX.

Si vous avez besoin d'imprimer beaucoup de nombres très rapidement, ne vous souciez pas d'allouer le tampon INT_MAX, puis d'imprimer à la place à plusieurs resockets. Moins de battement de mémoire est préférable.

Notez également que vous n'avez peut-être pas besoin du (double) par opposition à (float). Je n'ai pas pris la peine de vérifier. Passer en arrière comme ça peut aussi être un problème. YMMV sur tout ça. Fonctionne bien pour moi, cependant.

C’est bien que vous vous inquiétiez de la taille du tampon. Pour appliquer cette pensée en code, j’utiliserais snprintf

 snprintf( a, 256, "%d", 132 ); 

ou

 snprintf( a, sizeof( a ), "%d", 132 ); // when a is array, not pointer 

Tout d’abord, sprintf est le diable. Si quelque chose, utilisez snprintf, sinon vous risquez de détruire la mémoire et de planter votre application.

En ce qui concerne la taille du tampon, c’est comme tous les autres tampons – aussi petit que possible, aussi grand que nécessaire. Dans votre cas, vous avez un entier signé, prenez donc la plus grande taille possible et n’hésitez pas à append un peu de rembourrage de sécurité. Il n’y a pas de “taille standard”.

Cela ne dépend pas non plus du système sur lequel vous travaillez. Si vous définissez le tampon sur la stack (comme dans votre exemple), cela dépend de la taille de la stack. Si vous avez créé le thread vous-même, alors vous avez déterminé vous-même la taille de la stack, donc vous connaissez les limites. Si vous prévoyez une récursivité ou une trace de stack profonde, vous devez également faire très attention.