2016-10-08 22 views
10

Я бегу несколько тестов против моей функции itoa(), но получаюitoa() реализация с Int мин сгущенного

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

Необходимо ввести полный код для функции ft_intlen –

+0

, который является полным кодом, strdup просто выделяет строку, ft_intlen просто возвращает длину строки, оба передают тестовые примеры – franklinexpress

+0

Каков размер int в вашей системе? –

ответ

10

Эта линия:

if (x == -2147483648) 

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

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

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

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

#define INT_MIN (- INT_MAX - 1) 

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

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

+1

'if (x == -2147483648)' отлично от 'INT_MIN' может иметь другое значение. С C99/C11 '2147483648',' -2147483648' может поместиться как 'long long'. С C89 (и 32-разрядной длиной) '2147483648'' 'unsigned long'. Тип целочисленной константы является первым из соответствующего списка, в котором может быть представлено его значение. Unsuffixed decimal: int, long int, unsigned long INT;» C89 3.1.3.2 Целочисленные константы. Цитирование не применяется, так как '' '' '' '' '' '' '' '' с 'unsigned long'. «Если любой из операндов имеет тип unsigned long int, другой операнд преобразуется в unsigned long int». 3.2.1.5 – chux

+0

@chux Обычные арифметические преобразования фактически не говорят явно о том, как должны вести себя унарные операторы, но я проверял в gcc (как ни странно, '_Generic' работает в gcc в режиме c86), что преобразование соответствует подписанному int. Даже если равенство выполняется с unsigned long, это просто означает, что мы преобразуем значение, которое невозможно представить в unsigned long, и это еще раз реализация определена. – Art

+0

Преобразование 'int' в любой неподписанный тип хорошо определено, а не определено в реализации. «В противном случае, если новый тип без знака, значение преобразуется путем многократного добавления или вычитания более чем максимального значения, которое может быть представлено в новом типе, пока значение не будет в диапазоне нового типа». C11 6.3 Конверсии Итак, 'if (x == -2147483648)' становится 'if (((unsigned long) x) == 2147483648UL)' на 32-битных машинах C89. – chux

2

Статус: ПОСТАНОВИЛИ INVALID

Причина: 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 
+0

Я пытаюсь использовать его без ограничений. H, и с некоторыми скобками вокруг возврата есть соглашение. Это вполне приемлемо. – franklinexpress

+0

В (c) вы ((можете) писать (как)) много (в скобках (как (вы (хотите)))). Правда. Вопрос: полезно ли это? Дело в limit.h: Это по какой-то причине. Попытка сделать это без на самом деле не является приемлемым ответом. Потому что, если вы пытаетесь это сделать без, почему бы не просто malloc (12), а ft_intlen()? (Пожалуйста, не говорите, потому что вам нужно всего 8 байтов, сегодня каждый malloc возвращает n * 16 байтов с n, являющимся целым числом. Таким образом, вы все равно получаете 16 байтов. –

+0

Я просто говорю, что скобка должна «Даже не аргумент, это не имеет значения. – franklinexpress

0

Возможно проблема в вашем механизме предотвращения переполнения. Вы пытаетесь назначить 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 не один из system error numbers. Вам нужно добавить больше регистраций в ваше приложение, особенно если его невозможно отладить по какой-либо причине. Проверьте 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); 
} 
+0

Примечание: спецификация не гарантирует, что тип 'long long int' может обрабатывать диапазон значений большой, чем' int' eithe р. – chux

1

Вам не нужна эта проверка. Вместо того, чтобы преобразовать его в 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 аргументов.

+0

Я боюсь 'n = отрицательный? -x: x; 'вызывает неопределенное поведение, если' x == INT_MIN' из-за арифметического переполнения. – chqrlie

+0

@chqrlie: извините, забыли бросить на неподписанные. Исправлена. – ybungalobill

+0

'unsigned' обычно имеет примерно 2x диапазон как' int'. Per C spec 'INT_MAX == UINT_MAX' является возможностью. Это редкая ситуация, конвертирующаяся в 'unsigned', не дает правильных результатов с' INT_MIN'. – chux

0

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

  1. ft_strdup() как код называется с «Int минимального значения» и ошибка происходит.
  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); 
} 
0

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

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

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

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

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

0

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

INT_MIN 

вместо:

-2147483648 

в тесте:

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

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

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

#define INT_MIN (-INT_MAX - 1) 

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