itoa() c реализация в минимальном потоке

Я запускаю несколько тестов против моей функции 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);
}

7 ответов

Решение

Эта строка:

if (x == -2147483648)

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

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

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

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

#define INT_MIN (- INT_MAX - 1)

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

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

Статус: РАЗРЕШЕНО НЕДОПУСТИМО

Причина: WORKS_FOR_ME

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

  • sizeof(char) всегда 1, в этом нет необходимости.
  • не бросай malloc
  • если вы обрабатываете особый случай 0, то просто обработайте его за один раз.
  • -2147483648 очень очень плохо Это то что INT_MIN для.
  • возврат не является функцией, не возвращайте (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

Вам не нужна эта проверка. Вместо этого конвертируйте его в 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 аргументы.

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

Во всяком случае, строки:

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

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

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

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

INT_MIN

вместо:

-2147483648

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

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

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

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

#define INT_MIN  (-INT_MAX - 1)

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

Возможно проблема в вашем механизме предотвращения переполнения. Вы пытаетесь назначить 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);
}

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

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

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


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

Прохождение тестовых случаев не означает, что это сработало без вызова неопределенного поведения. Лучше всего опубликовать 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); 
}
Другие вопросы по тегам