Je me demandais si ma mise en œuvre d'une fonction "ITOA" est correcte. Peut-être que vous pouvez m'aider à me procurer un peu plus "correct", je suis à peu près sûr que je manque quelque chose. (Peut-être qu'il y a déjà une bibliothèque qui fait la conversion comme je le veux, mais ... Impossible de trouver)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> char * itoa(int i) { char * res = malloc(8*sizeof(int)); sprintf(res, "%d", i); return res; } int main(int argc, char *argv[]) { ...
12 Réponses :
Je pense que vous allouez peut-être trop de mémoire. MALLOC (8 * TAILLEOF (INT)) CODE> Vous donnera 32 octets sur la plupart des machines, ce qui est probablement excessif pour une représentation de texte d'un INT. P>
Tu as raison. Depuis 2 ^ 15 - 1, c'est en fait 32767, j'ai juste besoin de 6 * Taillef.
@Nicolas: Et un de plus pour le signe, si vous envisagez des nombres négatifs.
Merci ! (Ne peut pas encore +1, je ne suis pas encore inscrit) mais j'aurais!
Tailleof (int) * 3 + 2 CODE> doit toujours être suffisant (chaque octet contribue à 3 chiffres ou moins, et les 2 supplémentaires sont pour la résiliation du signe et de la null.
@R.: Pas sur les architectures où char_bit code> est supérieur à 9. par exemple. Certains DSP ont
char_bit == 32 code> et
Tailleof (int) == 1 code>.
@CAF: hein. Comment ça va pour quelque chose. Une chose est sûre, alors aide à défier mes idées préconçues. Merci!
Si vous souhaitez en tenir compte de cela, vous pouvez plutôt utiliser ((TYPE) - 1) / 3 + 2) CODE> pour les types signés ou
(((((((((((((caractères) Type))) / 3 + 1) code> pour les types non signés.
@CAF: Nice! Merci pour l'information supplémentaire! Être un gars .NET, je suis impressionné à quel point leur est d'apprendre lorsque vous plongez sous la touche .NET / Java / Python / etc abstraction.
C'est exactement pourquoi j'ai posé cette question; Même avec une question aussi simple que c'était, je ne pouvais pas penser à tous les cas, et mon style de programmation n'est pas encore brillant.
main() { int i=1234; char stmp[10]; #if _MSC_VER puts(_itoa(i,stmp,10)); #else puts((sprintf(stmp,"%d",i),stmp)); #endif return 0; }
Il y a quelques suggestions que je pourrais faire. Vous pouvez utiliser un tampon statique et STRUP pour éviter de répartir à plusieurs reprises une trop de mémoire sur les appels ultérieurs. J'ajouterais également une vérification des erreurs.
char *itoa(int i) { static char buffer[12]; if (snprintf(buffer, sizeof(buffer), "%d", i) < 0) return NULL; return strdup(buffer); }
Je ne suis pas tout à fait sûr où vous obtenez hth p> p> P> 8 * Tailleof (int) code> comme nombre maximal possible de caractères -
CEIL (8 / (log (10) / log (2 )))) code> donne un multiplicateur de
3 * code>. De plus, sous C99 et certaines plateformes de Posix plus anciennes, vous pouvez créer une version à l'allocation avec précision avec
sprintf () code>:
Deux appels à Snprintf semblent une mauvaise pratique.
@EvilClown: Eh bien, lorsque la chaîne de format est juste % d code> Ce n'est pas vraiment nécessaire, car vous pouvez déterminer l'espace maximum requis à la compilation. Pour un Int de 32 bits, il est 12, de sorte que vous ne vous inquiétez pas trop trop sur la suralimentation. Ce modèle général d'appel
snapintf code> deux fois est absolument des choses standard, cependant.
Ceci devrait fonctionner:
#include <string.h> #include <stdlib.h> #include <math.h> char * itoa_alloc(int x) { int s = x<=0 ? 1 ? 0; // either space for a - or for a 0 size_t len = (size_t) ceil( log10( abs(x) ) ); char * str = malloc(len+s + 1); sprintf(str, "%i", x); return str; }
On dirait que beaucoup de travail pour calculer la bonne taille lorsque le maximum est un 12 octets connu. CEIL, log10, ABS, deux variables temporaires (total de 8 octets) et une opération ternaire.
@EvilClown: Ouais, mais le processus général fonctionne pour tout entier de taille.
Échoue avec int_min code>.
Vous devez utiliser une fonction dans la famille printf code> à cette fin. Si vous écrivez le résultat sur
stdout code> ou un fichier, utilisez
printf code> /
fprintf code>. Sinon, utilisez
snprintf code> avec un tampon assez grand pour maintenir
3 * Tailleof (type) +2 code> octets ou plus. P>
La seule erreur réelle est que vous ne vérifiez pas la valeur de retour de Le nom si vous ne voulez pas compter sur votre plate-forme, je voudrais toujours conseiller après le motif. Les fonctions de manutention à cordes qui renvoient la mémoire nouvellement attribuée en C sont généralement plus de problèmes que ce qu'ils valent à long terme, car la plupart du temps, vous finissez par faire de nouvelles manipulations, vous devez donc vous libérer de nombreux résultats intermédiaires. Par exemple, comparez: p> vs. P> Si vous aviez la raison d'être particulièrement préoccupée par la performance (par exemple Si vous implémentez une bibliothèque de style STDLIB, y compris Voici une implémentation possible de MALLOC code> pour null.
ITOA code> est un peu déjà pris pour une fonction C'est non standard, mais pas rare. Il n'alloue pas de mémoire, mais il écrit à un tampon fourni par l'appelant: p>
itoa code>), ou si vous activez des bases que
sprintf code> ne prend pas en charge, vous pourriez envisager de ne pas appeler
sprintf code>. Mais si vous voulez une chaîne de base 10, votre premier instinct avait raison. Il n'y a absolument rien "incorrect" sur le spécificateur de format p>
itoa code>, pour la base 10 uniquement: p>
int itobase10n(char *buf, size_t sz, int value) {
return snprintf(buf, sz, "%d", value);
}
C'est idiot, s'il ne retourne pas la mémoire allouée, alors pourquoi ne pas simplement appeler Snprintf directement. SNPRINTF (EndPTR, octets, "% d", numéro) code> - Cela empêcherait une déclaration de variable temporaire et ne dispose pas de ces possibilités de dépassement de tampon dans ce code d'échantillon.
@EvilClown: la raison de ne pas appeler snaprintf code> directement est la même raison que quiconque écrit une fonction: supporter l'opération spécifiquement de convertir un entier à une chaîne, par opposition à l'une des nombreuses choses qui
SNPRINTF CODE> peut faire avec des formats de chaîne différents. Il n'y a qu'une possibilité de dépassement de tampon dans ce code sur les plates-formes où
int code> est d'au moins 48 bits, et je pense que ce problème a été couvert bien ailleurs. N'hésitez pas à fusionner un de ceux-ci dans cette réponse :-)
Quoi qu'il en soit, Snprintf est souvent sur-évalué. Si vous êtes inquiet des débordements de tampon et que votre solution consiste à utiliser SnPrintf, alors pourquoi ne vous inquiétez pas de tronquer la chaîne de résultat et de faire la mauvaise réponse? Un programme "sécurisé" qui ne fonctionne pas réellement est bien sûr meilleur qu'un programme non sécurisé qui ne fonctionne pas, mais toujours pas génial ;-p
@Steve, c'est pourquoi Snprintf retourne un négatif lorsqu'il y a une troncature et que vous vérifiez des erreurs. Le meilleur des deux mondes!
Standard snprintf code> ne revient pas négatif sur la troncature - c'est un pré-C99-ism par ex. Microsoft
_snprintf code> et très anciennes versions glibc. Il renvoie le nombre d'octets qui auraient été écrits, si la taille a été suffisamment grande. Vérification de la déclaration négative signifie que vous manquerez de la troncature sur les implémentations standard.
SNPRINTF CODE> est bon pour la "mesure, allouer, écrire", mais cela n'aide pas beaucoup avec des tampons courts imo. Et ceci est même avant de vous inquiéter de savoir si la longueur que vous avez adoptée est réellement correcte. La manipulation de la chaîne sûre en C est difficile.
Je n'ai pas réalisé que ce n'était pas standard, merci pour l'information. Je préférerais toujours voir Snprintf qu'une fonction qui envoie simplement Snprintf. Je comprends "abstraction" mais je ne le ferais pas dans ce cas. Recommanderiez-vous strcpy_strings_that_start_with_a code> au lieu de
Strcpy code>?
@EvilClown: Bien sûr que non, car les deux auraient exactement les mêmes paramètres et la mise en œuvre. Voulez-vous vous recommander d'éviter les bibliothèques HTTP, en faveur de la manipulation des prises directement? Nous pouvons jouer avec des extrêmes trompeurs indéfiniment - dans ce cas, le wrapper "supprime" un paramètre afin de créer un "inverse" à la fonction standard atoi code>, donc il ne fait pas beaucoup I> Comparé à l'utilisation directe de SNPRINTF, mais cela fait plus que rien. L'existence de
atoi code> fait
itoa code> une abstraction particulièrement naturelle.
Je suppose, finalement, je pense que cela vaut toujours la peine d'écrire quelles que soient les fonctions correspondent aux abstractions que l'appelant veut (dans la mesure du possible et la performance permettant), même si elles se retrouvent très similaires aux fonctions existantes. Si je me suis retrouvé souvent vouloir copier des chaînes qui commencent nécessairement avec a code>, je suppose peut-être que j'écrirais une fonction que
affirme code> le premier caractère puis appelle
strcpy < / code>. Sur une version de sortie, cela deviendrait un wrapper sans rien.
J'ai trouvé une ressource intéressante traitant de plusieurs problèmes différents avec la mise en œuvre de l'ITOA
Vous voudrez peut-être le chercher trop
ITOA () implémentations avec des tests de performance P>
Char * La version de mise en œuvre de 0,4 dans ce document est la meilleure mise en œuvre des performances.
@ADAM Le lien est cassé.
// Yet, another good itoa implementation // returns: the length of the number string int itoa(int value, char *sp, int radix) { char tmp[16];// be careful with the length of the buffer char *tp = tmp; int i; unsigned v; int sign = (radix == 10 && value < 0); if (sign) v = -value; else v = (unsigned)value; while (v || tp == tmp) { i = v % radix; v /= radix; if (i < 10) *tp++ = i+'0'; else *tp++ = i + 'a' - 10; } int len = tp - tmp; if (sign) { *sp++ = '-'; len++; } while (tp > tmp) *sp++ = *--tp; return len; } // Usage Example: char int_str[15]; // be careful with the length of the buffer int n = 56789; int len = itoa(n,int_str,10);
Il manque '\ 0', soyez prudent lorsque vous l'utilisez
@Thomas, je viens de modifier la fonction pour renvoyer la longueur de la chaîne créée.
Votre réclamation sur v / = radix code> étant plus rapide que
v = v / radix code> n'est pas vrai. Tout compilateur doit produire le même code pour les deux formulaires. Je l'ai testé dans GCC avec des optimisations désactivées.
Ajout * sp = '\ 0'; code> avant
retour len; code> de sorte que
sp code> sera une chaîne ASCII corrigée correctement terminée que vous pouvez utiliser dans des fonctions telles que
strcpy code>.
Un bon adapter le traitement des erreurs au besoin. (besoin C99 ou plus tard): P> int code> à string em> ou
itoa () code> a ces propriétés;
[int_min ... int_max] code>, base
[2 ... 36] code> sans débordement de tampon. LI>
int code>. li>
non signé code> pour avoir une plus grande plage positive que
int code>. En d'autres termes, n'utilise pas
non signé code>. li>
'-' code> pour des nombres négatifs, même lorsque
base! = 10 code>. li>.
ul>
char* itostr(char *dest, size_t size, int a, int base) {
// Max text needs occur with itostr(dest, size, INT_MIN, 2)
char buffer[sizeof a * CHAR_BIT + 1 + 1];
static const char digits[36] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (base < 2 || base > 36) {
fprintf(stderr, "Invalid base");
return NULL;
}
// Start filling from the end
char* p = &buffer[sizeof buffer - 1];
*p = '\0';
// Work with negative `int`
int an = a < 0 ? a : -a;
do {
*(--p) = digits[-(an % base)];
an /= base;
} while (an);
if (a < 0) {
*(--p) = '-';
}
size_t size_used = &buffer[sizeof(buffer)] - p;
if (size_used > size) {
fprintf(stderr, "Scant buffer %zu > %zu", size_used , size);
return NULL;
}
return memcpy(dest, p, size_used);
}
sprintf est assez lent, si la performance compte, ce n'est probablement pas la meilleure solution.
Si l'argument de base est une puissance de 2 la conversion peut être effectuée avec un décalage et un masquage, et on peut éviter d'inverser la chaîne par enregistrer les chiffres des positions les plus élevées. Par exemple, quelque chose comme ceci pour la base = 16 p> Const chiffre chiffre [] = {'0', '1', '2', '3', '4', '4' ',' 5 ',' 6 ',' 7 ',' 8 ',' 9 ',' 9 ',' A ',' B ',' B ',' C ',' E ',' E ',' F '}; P > /* skip zeros in the highest positions */
int i = num_iter;
for (; i >= 0; i--)
{
int digit = (value >> (bits_per_digit*i)) & 15;
if ( digit > 0 ) break;
}
for (; i >= 0; i--)
{
int digit = (value >> (bits_per_digit*i)) & 15;
result[len++] = digits[digit];
}
. P>
int32_t my_atoi(uint8_t *ptr, uint8_t digits, uint32_t base){ int32_t sgnd=0, rslt=0; for(int i=0; i<digits; i++){ if(*(ptr)=='-'){*ptr='0';sgnd=1;} else if(*(ptr+i)>'9'){rslt+=(*(ptr+i)-'7');} else{rslt+=(*(ptr+i)-'0');} if(!*(ptr+i+1)){break;} rslt*=base; } if(sgnd){rslt=-rslt;} return rslt; }
. p>
Assez lent je ferais cela avec une récursion
D'accord, vous pouvez trouver ici une analyse plus détaillée: strudel.org.uk/itoa
Fais attention. Il n'y a rien à indiquer que l'utilisateur doit supprimer le pointeur de retour après.
Connexes: Stackoverflow.com/q/4351371/103167
Vous n'avez pas mis en œuvre
itoa code>. Vous venez de transférer son travail à la fonction de bibliothèque
sprintf code> qui fait le travail pour vous.