itoa () C implementation in min underflow

je fais quelques essais avec ma fonction itoa () mais je continue à avoir

did not allocate memory for the int min value

je fais l'addition mais c'est quelque chose que je manque ici, qu'est-ce que c'est?

char *ft_itoa(int x) {
    char *s;
    size_t len;
    long int n;

    n = x;
    if (x == -2147483648)
        return (ft_strdup("-2147483648"));

    len = ft_intlen(n) + 1;
    if (!(s = (char*)malloc(sizeof(char) * len)))
        return (NULL);

    if (n == 0)
        s[0] = '0';

    if (n < 0) {
        s[0] = '-';
        n = -n;
    }
    s[len - 1] = '';
    while (n) {
        len--;
        s[len - 1] = (n % 10) + '0';
        n /= 10;
    }
    return (s);
}
10
demandé sur Peter Varo 2016-10-08 10:42:37

7 réponses

Cette ligne:

if (x == -2147483648)

ne fait pas ce que vous pensez qu'il fait. C n'a pas de constantes entières négatives. Il s'agit d'une constante int non signée avec la valeur 2^31, sur laquelle vous appliquez l'opérateur unary minus. Cela signifie que l'expression x == -21... dépendra de la norme C votre compilateur utilise.

si vous utilisez C99 ou C11, tout ira bien. Il y a un type signé qui est assez grand - long long est garanti d'être assez grand pour ce nombre, donc à la fois x et -21... sera être converti en long, puis de les comparer. Mais si vous utilisez un compilateur C89 et que votre machine n'a pas un type assez long, vous rencontrez ici un comportement défini par l'implémentation:

Lorsqu'un entier est rétrogradé à un entier signé de plus petite taille, ou qu'un entier non signé est converti en son entier signé correspondant, si la valeur ne peut pas être représentée, le résultat est défini par implémentation.

C'est pourquoi les gens disent à utiliser limite.H. Non pas parce qu'ils sont pédants, mais parce que c'est un territoire dangereux. Si vous regardez attentivement quelles limites.h contient, vous aurez plus de chances de trouver une ligne comme ceci:

#define INT_MIN (- INT_MAX - 1)

cette expression a en fait le type et la valeur corrects.

a part ça, Je ne vois aucune erreur dans le code que vous avez posté. Si ce n'est pas le problème ft_intlen ou ft_strdup sont mauvais. Ou vous appelez votre fonction dans testing incorrect (les mêmes problèmes s'appliquent à -21... lorsque l'appel de tests).

10
répondu Art 2016-10-10 13:45:54

statut: résolu invalide

raison: WORKS_FOR_ME

de toute façon, je me suis amélioré sur certains points.

  • sizeof(char) est toujours de 1, pas besoin.
  • ne pas cast malloc
  • si vous traitez le cas spécial 0, alors traitez-le en une seule fois.
  • -2147483648 est très très mauvais. C'est ce que INT_MIN.
  • retour n'est pas une fonction, n'est pas de retour (value), il suffit de revenir value.
  • ne pas s[len - 1] tous les temps, mieux décrémente len avant d'entrer dans la boucle. Ou, puisque vous avez besoin d' len + 1 dans le malloc appel, il suffit d'avoir lenintlen renvoie et appelle malloc en utilisant len + 1

ft_itoa.c

#include <stdbool.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <btstr.h>

int ft_intlen(int n) {
        char buffer[8192];
        return snprintf(buffer, sizeof buffer, "%i", n);
}

char * ft_itoa(int n) {
        char * s;
        size_t l, len;
        bool fix_int_min = false;

        if (!n) {
                return mstrcpy("0");
        }

        if (-INT_MAX != INT_MIN && n == INT_MIN) {
                ++n;
                fix_int_min = true;
        }

        len = ft_intlen(n);
        if (!(s = malloc(len + 1))) {
                return NULL;
        }
        if (n < 0) {
                s[0] = '-';
                n = -n;
        }
        s[l = len] = '';
        while (n) {
                s[--len] = (n % 10) + '0';
                n /= 10;
        }

        if (fix_int_min) {
                --l;
                while (s[l] == '9') {
                        s[l++] = 0;
                }
                if (s[l] == '-') {
                        // realloc +1 and write "-1[0....0]"
                } else {
                        ++s[l];
                }
        }

        return s;
}

main.c

#include <limits.h>
#include <stdio.h>

char * ft_itoa(int n);

void check(int n) {
        printf("%i = %s\n", n, ft_itoa(n));
}

int main() {
        check(0);
        check(-1);
        check(1);
        check(23);
        check(42);
        check(4711);
        check(1000);
        check(INT_MAX);
        check(1+INT_MIN);
        check(INT_MIN);
}

Résultat

$ gcc -W -Wall -Wextra -lBtLinuxLibrary ft_itoa.c main.c -o ft_itoa && ./ft_itoa
0 = 0
-1 = -1
1 = 1
23 = 23
42 = 42
4711 = 4711
1000 = 1000
2147483647 = 2147483647
-2147483647 = -2147483647
-2147483648 = -2147483648
2
répondu Bodo Thiesen 2016-10-08 09:25:50

Vous n'avez pas besoin de vérifier. Convertissez-le plutôt en unsigned, qui correspondra à la valeur absolue :

size_t ft_uintlen(unsigned n)
{
    size_t len = 0;
    do {
        ++len;
        n /= 10;
    } while(n);
    return len;
}

char *ft_itoa(int x)
{
    char    *s;
    size_t  len;
    unsigned n;
    int negative;

    negative = x < 0;
    n = negative ? 0-(unsigned)x : (unsigned)x;
    len = ft_uintlen(n) + negative + 1;
    if (!(s = (char*)malloc(len)))
        return (NULL);

    s[--len] = '';
    if (negative)
        s[0] = '-';
    do {
        s[--len] = (n % 10) + '0';
        n /= 10;
    } while(n);
    return (s);
}

notez que ceci utilise un nouveau size_t ft_uintlen(unsigned) fonction qui fonctionne sur unsigned arguments.

1
répondu ybungalobill 2016-10-14 18:25:00

Probablement problème dans votre dépassement de mécanisme de prévention. Vous essayez d'attribuer x de type intn avec le type long int. Mais la spécification ne garantit pas ce type long int peut gérer la gamme de valeur de grandes alors int. Plus d'informations peuvent être trouvées " Long Vs. Int"!--17-->.

Utiliser long long int tapez n si votre compilateur le supporte. Mise à jour de votre ft_intlen function int ft_intlen(long long int n). Dans ce cas, vous serez en mesure de gérer l'ensemble du int valeur de type gamme et supprimer les lignes suivantes:

if (x == -2147483648)
  return (ft_strdup("-2147483648"));  

aussi message d'erreur did not allocate memory for the int min value n'est pas l'un de l' numéros d'erreur du système. Vous devez ajouter plus de journalisation dans votre application, surtout s'il n'est pas possible de la Déboguer pour une raison quelconque. Vérifier errno pour chaque appel de fonction système, par exemple:

char* errmsg;
// Other code skipped here 
if (!(s = (char*)malloc(sizeof(char) * len)))
{
  errmsg = strerror(errno);          // Use strerror_s if possible 
  printf("Malloc error: %s\n", errmsg);
  return (NULL);
}
0
répondu Nikita 2017-05-23 10:32:30

Echec de codes potentiels, par ordre de suspicion:

  1. ft_strdup() que ce code est appelé avec "int valeur min" et l'erreur se produit.
  2. Prototypes manquant pour diverses fonctions. Surtout ft_strdup()/strdup().
  3. le code D'appel/d'essai est défectueux.
  4. "int valeur min" est plus grand que -2147483648. (Il est mieux d'utiliser INT_MIN.)
  5. ft_intlen(n) est mal codé et retourne INT_MAX, puis le code essaie malloc(INT_MIN).
  6. int/long les deux 64 bits. Cela crée le premier s[len - 1] = (n % 10) + '0';INT_MIN.

Sinon, si INT_MIN a la valeur de -2147483648, ft_itoa(int x) c'est bien.


OP asserts"... strdup attribue juste la chaîne, ft_intlen renvoie juste la longueur de la chaîne, les deux passent les cas de test – franklinexpress Oct 8 à 7: 52"

la réussite des cas de test ne signifie pas qu'il a fonctionné sans invoquer un comportement non défini. Le meilleur de la poste ft_intlen(),ft_strdup() et le harnais d'essai pour examen.


Candidat portable mise en oeuvre. Pas de dépendance à <limits.h> mis à part CHAR_BIT quel code supposons que 8 est sans sacrifier trop de potentialité. Fonctionne avec C89 / 99 / 11.

// Buffer size needed to decimal print any `int`
// '-' + Ceiling(value bit size * log10(2)) + 
#define INT_STR_SIZE (1 + ((CHAR_BIT*sizeof(int) - 1)/3 + 1) + 1)

char *ft_itoa(int x) {
  char buf[INT_STR_SIZE];
  char *s = buf + sizeof buf - 1;  // Set to end of buffer
  *s = '';

  int n = x; // no need for wider types like long

  if (n > 0) {
    // fold positive numbers to negative ones
    // This avoids the special code for `INT_MIN` and need for wider types
    n = -n;
  }

  // Using a do loop avoids special code for `x==0`
  do {
    // Use `div()` rather than / % in case we are using C89.
    // / %  has implementation defined results for negative arguments.
    div_t qr = div(n, 10);
    *--s = (char) ('0' - qr.rem);  // Form digit from negative .rem
    n = qr.quot;
  } while (n);

  if (x < 0) {
    *--s = '-';
  }

  // Double check ft_strdup() is coded correctly
  // Insure calling code frees the buffer when done.
  return ft_strdup(s); 
}
0
répondu chux 2016-10-13 22:30:11

le morceau de code que vous avez donné compile et fonctionne sur OsX, mais avec mon propre ft_stdup et ft_intlen. Ainsi, vous pouvez nous montrer le code ou les vérifier pour les erreurs. J'ai fait quelques tests (dont 2147483647, -2147483648). Il fonctionne très bien.

de toute façon, les lignes suivantes:

if (x == -2147483648) return (ft_strdup("-2147483648"));

Sont inutiles tant que vous copiez votre x valeur long long variable ( Art) avant toute opération. Si vous n'avez pas besoin, y compris types.h (la célèbre moulinette ne vous donnera pas -42).

il arrive que sur OsX il fonctionne aussi sur long valeurs, mais c'est un coffre-fort non portable.

0
répondu LotoLo 2017-05-23 12:10:54

utilisation:

INT_MIN

au lieu de:

-2147483648

dans votre test:

if (x == INT_MIN)
    return (ft_strdup("-2147483648"));

la raison en est que certains compilateurs pourraient avoir de la difficulté à comprendre ce nombre.

La bibliothèque standard C limites.h habituellement, définissez - le comme suit:

#define INT_MIN  (-INT_MAX - 1)

pour éviter ce problème.

0
répondu FloppySoftware 2016-10-17 10:09:49