#c
#c
Вопрос:
Я ввел этот блок кода для назначения:
char *tokens[10];
void parse(char* input);
void main(void)
{
char input[] = "Parse this please.";
parse(input);
for(int i = 2; i >= 0; i--) {
printf("%s ", tokens[i]);
}
}
void parse(char* input)
{
int i = 0;
tokens[i] = strtok(input, " ");
while(tokens[i] != NULL) {
i ;
tokens[i] = strtok(NULL, " ");
}
}
Но, глядя на это, я не уверен, как работает распределение памяти. Насколько я знаю, я не определял длину отдельных строк, просто сколько строк находится в токенах массива строк (10). Есть ли у меня это наоборот? Если нет, то выделяет ли компилятор длину каждой строки динамически? Требуется некоторое разъяснение.
Ответ №1:
strtok
является плохим гражданином.
Во-первых, он сохраняет состояние, которое вы неявно использовали при вызове strtok(NULL,...)
— это состояние сохраняется в частной памяти стандартной библиотеки C, что означает, что могут использовать только однопоточные программы strtok
. Обратите внимание, что в некоторых библиотеках существует вызываемая реентерабельная версия strtok_r
.
Во-вторых, и, чтобы ответить на ваш вопрос, strtok
изменяет его входные данные. Он не выделяет пространство для строк; он записывает символы NUL вместо вашего разделителя во входной строке и возвращает указатель на входную строку.
Вы правы в том, что strtok
может возвращать более 10 результатов. Вы должны проверить это в своем коде, чтобы не писать дальше конца tokens
. Надежная программа либо установила бы верхний предел, как у вас 10
, и проверила бы его, сообщив об ошибке, если он превышен, либо динамически выделила бы tokens
массив с malloc
и realloc
им, если он становится слишком большим. Затем ошибка возникает, когда вам не хватает памяти.
Обратите внимание, что вы также можете обойти проблему strtok
изменения вашей входной строки путем strdup
редактирования перед передачей ее в strtok
. Затем вам придется освободить новую строку после того, как она и tokens
, которая указывает на нее, выйдут за пределы области видимости.
Комментарии:
1. О, хорошо, спасибо, приятно это знать. Итак, я думал об этом неправильно.
tokens
это не двумерный массив символов, а массив указателей. Таким образом, компилятор выделяет для sizeof(void *) не для нескольких символов.2. @Doug Currie Следует отметить, что strdup не является стандартной библиотечной функцией C и, вероятно, недоступна на платформах, которые не совместимы с POSIX.
Ответ №2:
tokens
это массив указателей.
Различие между строками и указателями часто нечеткое. В некоторых ситуациях строки лучше представлять как массивы, в других ситуациях — как указатели.
В любом случае … в вашем примере input
это массив, а tokens
это массив указателей на место внутри input
.
Данные внутри input
изменяются при каждом вызове strtok()
Итак, шаг за шагом
// input[] = "foo bar baz";
tokens[0] = strtok(input, " ");
// input[] = "foobar baz";
// ^-- tokens[0] points here
tokens[1] = strtok(NULL, " ");
// input[] = "foobarbaz";
// ^-- tokens[1] points here
tokens[2] = strtok(NULL, " ");
// input[] = "foobarbaz";
// ^-- tokens[2] points here
// next strtok returns NULL