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)
чтобы избежать этой проблемы.