#c
Вопрос:
При отладке простого приложения оболочки я столкнулся со странной ошибкой, когда команда имеет >2 параметров. Я отследил его до этой функции (строка [] — это командная строка, например echo one two
, а sep-символ для разделения строки, установленный в ' '
том месте, где она вызывается).:
char **split(char string[], char *sep) {
char *token = strtok(string, sep);
char **argv = calloc(1, sizeof(char*));
int i = 0;
while (token != NULL) {
argv = realloc(argv, sizeof(argv) sizeof(char*));
argv[i] = calloc(strlen(token), sizeof(char));
strcpy(argv[i ], token);
token = strtok(NULL, sep);
}
argv[i] = NULL; // sets argv[0] even if i == 4
return argv;
}
Это прекрасно работает с Однако с 3 параметрами argv[0]
в конечном итоге устанавливается значение null в конце вместо последнего элемента.
Я делаю что-то глупое или есть неопределенное поведение?
изменить: >=4 параметра вызывают сбой программы:
realloc(): invalid next size
signal: aborted (core dumped)
Ответ №1:
В вашем коде три ошибки.
strlen()
задает длину строки без нулевого терминатора. Так что вам придется писатьcalloc(strlen(token) 1, sizeof(char));
, чтобы освободить для этого место.sizeof(argv)
возвращает размер указателя (а не количество элементов) и является постоянным во время компиляции.argv[i] = NULL;
это выходит за рамки
Рабочий код:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **split(char string[], char *sep)
{
char *token = strtok(string, sep);
char **argv = calloc(1, sizeof(char *));
int i = 0;
while(token != NULL)
{
argv = realloc(argv, (i 1) * sizeof(char *));
argv[i] = calloc(strlen(token) 1, sizeof(char));
strcpy(argv[i ], token);
token = strtok(NULL, sep);
}
argv = realloc(argv, (i 1) * sizeof(char *));
argv[i] = NULL;
return argv;
}
int main(void)
{
char s[] = "hello world test word";
char **result = split(s, " ");
printf("[0] = %sn", result[0]);
printf("[1] = %sn", result[1]);
printf("[2] = %sn", result[2]);
printf("[3] = %sn", result[3]);
return 1;
}
Я рекомендую использовать gdb и valgrind для отладки таких проблем.
Ответ №2:
Проблема заключается в этих двух линиях:
argv = realloc(argv, sizeof(argv) sizeof(char*));
argv[i] = calloc(strlen(token), sizeof(char));
sizeof(argv)
указывает размер указателя (обычно 4 или 8), а не количество элементов, на которые он указывает. Поэтому, когда вы звоните realloc
, вы всегда просите одно и то же количество байтов, которого, оказывается, достаточно для двух элементов массива. Затем, когда у вас больше 2, вы записываете за пределы выделенной памяти, вызывая неопределенное поведение.
Так i
как отслеживается длина массива, используйте это при вызове realloc
, и вам нужно умножить на размер элемента, а не добавлять.
argv = realloc(argv, (i 1) * sizeof(char*));
В следующей строке вы выделяете достаточно места для символов в строке, но недостаточно для завершающего байта null, поэтому добавьте 1 для этого:
argv[i] = calloc(strlen(token) 1, sizeof(char));