Вопрос или проблема
Я пытаюсь создать утилиту командной строки, и не понимаю, почему у меня возникает ошибка сегментации, когда я пытаюсь токенизировать строку, введенную пользователем.
В файле str_utils.c
выражение printf("token: %s\n", tokens[i]);
оценивается правильно, и содержимое массива выводится, но в файле user_input.c
, если я печатаю указатель с помощью %p
, все в порядке, но как только я пытаюсь вывести с помощью %s
, возникает ошибка сегментации. Похоже, что я не удаюсь сохранить отдельный токен в массиве tokens
.
Вот код:
// str_utils.c
int str_utils_split(char *str, char *delimiter, char **tokens,
int *tokens_count) {
char *local_str = strdup(str);
for (char *token = strtok(local_str, delimiter); token != NULL;
token = strtok(NULL, delimiter)) {
tokens = realloc(tokens, sizeof(char *) * (++(*tokens_count)));
if (tokens == NULL) {
fprintf(stderr, "realloc failed! Exiting!");
return 1;
}
/* Я делаю что-то не так здесь? Я пришел к этому, потому что
я читал, что strtok не выполняет никакого выделения памяти
*/
tokens[*tokens_count - 1] =
strcpy((char *)calloc(strlen(token) + 1, sizeof(char)), token);
}
// или там?
tokens = realloc(tokens, sizeof(char *) * (*tokens_count + 1));
tokens[*tokens_count + 1] = 0;
for (int i = 0; i < *tokens_count; i++) {
printf("tokenin called fn: %s\n", tokens[i]);
}
free(local_str);
return 0;
}
// user_input.c
char command_line_buffer[100];
void ui_parse_command(char *line, ui_command_t *command_obj) {
char **cmd_tokens = malloc(sizeof(char *));
char *delimiter = " ";
int tokens_count = 0;
str_utils_split(line, delimiter, cmd_tokens, &tokens_count);
for (int i = 0; i < tokens_count; i++) {
printf("token in calling fn: %p\n", cmd_tokens[i]); // ОК
printf("token in calling fn: %s\n", cmd_tokens[i]); // ошибка сегментации
}
}
void ui_start_prompt(void (*callback)(ui_command_t *)) {
printf("auralizer> ");
ui_command_t command_obj;
while (fgets(command_line_buffer, sizeof(command_line_buffer), stdin) !=
NULL) {
ui_parse_command(command_line_buffer, &command_obj);
callback(&command_obj);
printf("auralizer> ");
}
}
Я пробовал с локальными копиями и без них, с локальным массивом tmp_tokens
в функции разделения и без него, я пытался начать с NULL указателя в user_input.c
, я повернул это во всех направлениях, и оно всегда компилируется, но всегда приводит к ошибке сегментации в одном и том же месте.
Правильный вывод был бы (правильный вывод произошел только один раз и снова произошел сбой после того, как я перезапустил программу):
auralizer> tralala lala
token in called fn: tralala
token in called fn: lala
token in calling fn: tralala
token in calling fn: lala
А после повторного выполнения программы (printf("%s")
):
auralizer> tralala lala
token in called fn: tralala
token in called fn: lala
[1] 27102 ошибка сегментации (сброс ядра) ./bin/auralizer
Печать указателей (printf("%p")
):
auralizer> tralala lala
token in called fn: tralala
token in called fn: lala
token in calling fn: 0x557717df4cb0
token in calling fn: 0x557717df4cd0
Как я мог бы это исправить? Есть идеи? Большое спасибо!
Ответ или решение
Для понимания проблемы с сегментационной ошибкой (segmentation fault), которая возникает в вашем коде после токенизации строк, необходимо рассмотреть несколько ключевых аспектов. Программный код состоит из двух файлов: str_utils.c
, где происходит токенизация, и user_input.c
, который инициирует процесс токенизации и использует токены после их создания.
Проблема Сегментационной Ошибки
Согласно предоставленному коду, основная проблема возникает из-за неправильного управления памятью, что влечет за собой влияние на указатели на создаваемые токены. В частности, функция str_utils_split
пытается передать указатель tokens
обратно в вызывающую функцию (ui_parse_command
), но модификация указателя внутри функции не отражается на левом указателе (в user_input.c
).
Аспекты, способствующие ошибке:
-
Передача указателя в функцию:
Когда вы передаетеcmd_tokens
вstr_utils_split
, на самом деле вы передаете копию указателя, а не сам указатель. Из-за этого любые изменения, сделанные сtokens
внутриstr_utils_split
, не затрагиваютcmd_tokens
вui_parse_command
. -
Переполнение памяти:
Вы выделяете память дляtokens
, но не обеспечиваете правильное возвращение выделенной памяти. Вместо того чтобы изменять сам указательtokens
, необходимо использовать двойной указатель (char ***tokens
), чтобы обеспечить правильное изменение. -
Ошибочная окончательная инициализация токенов:
В вашей функцииstr_utils_split
имеется строка, которая выделяет дополнительный байт для установки нулевого окончания, однако вы используете неправильную индексацию при его установке.
Решение Проблемы
Для исправления кода и избежания сегментационных ошибок, потребуется внести изменения в структуру передачи указателей и управления памятью.
Шаги по исправлению
-
Измените сигнатуру функции
str_utils_split
:int str_utils_split(char *str, char *delimiter, char ***tokens, int *tokens_count) {
-
Вызовите функцию, передавая адрес указателя
cmd_tokens
:str_utils_split(line, delimiter, &cmd_tokens, &tokens_count);
-
В теле функции
str_utils_split
измените выделение памяти и установку токенов:*tokens = realloc(*tokens, sizeof(char *) * (*tokens_count));
-
Убедитесь, что при добавлении нулевого завершения вы используете правильную индексацию:
(*tokens)[*tokens_count] = NULL; // Чтобы указатель был корректно завершен
Переписанный Код
Вот как может выглядеть исправленный фрагмент вашего кода:
// str_utils.c
int str_utils_split(char *str, char *delimiter, char ***tokens,
int *tokens_count) {
char *local_str = strdup(str);
for (char *token = strtok(local_str, delimiter); token != NULL;
token = strtok(NULL, delimiter)) {
*tokens = realloc(*tokens, sizeof(char *) * (++(*tokens_count)));
if (*tokens == NULL) {
fprintf(stderr, "realloc failed! Exiting!");
free(local_str);
return 1;
}
(*tokens)[*tokens_count - 1] = strcpy(calloc(strlen(token) + 1, sizeof(char)), token);
}
*tokens = realloc(*tokens, sizeof(char *) * (*tokens_count + 1));
(*tokens)[*tokens_count] = NULL; // Корректное завершение указателя
free(local_str);
return 0;
}
// user_input.c
void ui_parse_command(char *line, ui_command_t *command_obj) {
char **cmd_tokens = NULL; // Начальное значение NULL
char *delimiter = " ";
int tokens_count = 0;
str_utils_split(line, delimiter, &cmd_tokens, &tokens_count);
for (int i = 0; i < tokens_count; i++) {
printf("token in calling fn: %s\n", cmd_tokens[i]); // Теперь должно работать без ошибки
free(cmd_tokens[i]); // Не забудьте освободить выделенную память
}
free(cmd_tokens); // Освобождаем основной массив после использования
}
Заключение
Теперь ваш код должен работать корректно, без сегментационных ошибок. Важно следить за управлением памятью и передачей многомерных указателей для предотвращения подобных проблем в будущем. Будьте внимательны с индексацией и выделением памяти, чтобы код работал надежно и без ошибок.