Itoa() c реализация int min underflow

Я запускаю несколько тестовых примеров против моей функции itoa(), но продолжаю получать

did not allocate memory for the int min value

Я делаю чек, но это то, что мне здесь не хватает, что это такое?

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] = '\0';
    while (n) {
        len--;
        s[len - 1] = (n % 10) + '0';
        n /= 10;
    }
    return (s);
}

Ответ 1

Эта строка:

if (x == -2147483648)

не делает то, что вы думаете. C не имеет отрицательных целых констант. Это unsigned int constant со значением 2 ^ 31, на который вы применили унарный оператор минус. Это означает, что выражение x == -21... будет зависеть от стандарта C, который использует ваш компилятор.

Если вы используете C99 или C11, все будет в порядке. Существует большой тип подписанного типа - длинный длинный гарантированно будет достаточно большим для этого числа, поэтому и x, и -21... будут преобразованы в длинный, а затем сравнимый. Но если вы используете компилятор C89, и ваш компьютер не имеет достаточно длинного типа, вы здесь видите поведение, определяемое реализацией:

Когда целое число понижается до знакового целого с меньшим размером или целое число без знака преобразуется в соответствующее соответствующее целое число со знаком, если значение не может быть представлено, результат определяется реализацией.

Вот почему люди говорят, что используют limits.h. Не потому, что они педантичны, а потому, что это опасная территория. Если вы внимательно посмотрите, что содержит лимиты. H, вы, скорее всего, найдете такую ​​строку:

#define INT_MIN (- INT_MAX - 1)

Это выражение действительно имеет правильный тип и значение.

Кроме этого, я не вижу ошибок в отправленном вами коде. Если это не проблема, то ошибки ft_intlen или ft_strdup неверны. Или вы вызываете свою функцию при неправильном тестировании (те же проблемы применяются к -21... при вызове тестов).

Ответ 2

Статус: RESOLVED INVALID

Причина: WORKS_FOR_ME

В любом случае, я улучшился в некоторых точках.

  • sizeof(char) всегда 1, не нужно.
  • не бросать malloc
  • если вы обрабатываете специальный случай 0, тогда просто обрабатывайте его за один раз.
  • -2147483648 очень плохо. Для чего INT_MIN.
  • return не является функцией, не возвращайте (value), просто верните value.
  • не все s[len - 1] все время, лучше уменьшать len до входа в цикл. Или, так как вам нужно len + 1 только в вызове malloc, просто len, поскольку intlen возвращает его и вызывает malloc, используя 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] = '\0';
        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]\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);
}

Результат

$ 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

Ответ 3

Вам не нужна эта проверка. Вместо этого преобразуйте его в unsigned, который будет соответствовать абсолютному значению:

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] = '\0';
    if (negative)
        s[0] = '-';
    do {
        s[--len] = (n % 10) + '0';
        n /= 10;
    } while(n);
    return (s);
}

Обратите внимание, что здесь используется новая функция size_t ft_uintlen(unsigned), которая работает с аргументами unsigned.

Ответ 4

Вероятно, проблема в вашем механизме предотвращения переполнения. Вы пытаетесь назначить x типа int на n с типом long int. Но спецификация не гарантирует, что тип long int может обрабатывать диапазон значений большой, чем int. Более подробную информацию можно найти "Long Vs. Int" .

Используйте long long int для n, если ваш компилятор поддерживает его. Обновите функцию ft_intlen до int ft_intlen(long long int n). В этом случае вы сможете обрабатывать весь диапазон значений типа int и удалять следующие строки:

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

Также сообщение об ошибке did not allocate memory for the int min value не относится к числу системных ошибок . Вам нужно добавить больше регистраций в ваше приложение, особенно если это невозможно отладить по какой-либо причине. Проверьте errno для каждого вызова системной функции, например:

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);
}

Ответ 5

Потенциальные ошибки кода в порядке подозрения:

  • ft_strdup(), так как этот код вызывается с "int min value" и возникает ошибка.
  • Прототипы отсутствуют для различных функций. Особенно ft_strdup()/strdup().
  • Код вызова/проверки неисправен.
  • Значение "int min" больше, чем -2147483648. (Лучше использовать INT_MIN.)
  • ft_intlen(n) неправильно закодирован и возвращает INT_MAX, затем код пытается malloc(INT_MIN).
  • int/long обе 64-разрядные. Это помещает первый s[len - 1] = (n % 10) + '0'; с INT_MIN.

В противном случае, если INT_MIN имеет значение -2147483648, ft_itoa(int x) отлично.


OP утверждает: "... strdup просто выделяет строку, ft_intlen просто возвращает длину строки, оба передают тестовые примеры - franklinexpress Oct 8 at 7:52"

Передача тестовых примеров не означает, что он работал без вызова поведения undefined. Лучше всего отправить ft_intlen(), ft_strdup() и проверить жгут для проверки.


Переносная реализация кандидата. Нет зависимости от размера int/long или 2 дополнения. Нет необходимости в <limits.h> помимо CHAR_BIT, который может принять код, 8, не жертвуя слишком большой способностью. Работает с C89/99/11.

// Buffer size needed to decimal print any `int`
// '-' + Ceiling(value bit size * log10(2)) + \0
#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 = '\0';

  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); 
}

Ответ 6

Кусок кода, который вы компилировали и работает на OsX, но с моими собственными ft_stdup и ft_intlen. Таким образом, вы можете либо показать нам код, либо проверить их на наличие ошибок. Я провел несколько тестов (включая 2147483647, -2147483648). Он работает красиво.

В любом случае, строки:

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

Бесполезны, если вы скопируете значение x в переменную long long (Искусство), прежде чем выполнять какую-либо операцию. Поэтому вам не нужно включать types.h (пресловутая муленета не даст вам -42).

Бывает, что на OsX он также работает на значениях long, но это не переносимо.

Ответ 7

Просто используйте:

INT_MIN

вместо:

-2147483648

в вашем тесте:

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

Причина этого в том, что некоторые компиляторы могут иметь проблемы с пониманием этого числа.

Стандартная библиотека C limits.h обычно определяет ее как:

#define INT_MIN  (-INT_MAX - 1)

чтобы избежать этой проблемы.