Как компилятор выделяет память для массива строк в C?

#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