Как правильно использовать strtok в C, чтобы не было утечки памяти?

Меня несколько смущает то, что происходит, когда вы вызываете strtok для указателя char в C. Я знаю, что он изменяет содержимое строки, поэтому, если я вызову strtok для переменной с именем 'line', ее содержимое изменится. Предположим, я следую следующему подходу:

void function myFunc(char* line) {

    // get a pointer to the original memory block
    char* garbageLine = line;

    // Do some work
    // Call strtok on 'line' multiple times until it returns NULL
    // Do more work

    free(garbageLine);
}

Далее предположим, что 'line' распределена перед тем, как она будет передана в myFunc. Должен ли я освобождать исходную строку после использования strtok или она делает эту работу за нас? Кроме того, что произойдет, если «линия» не будет распределена, и я попытаюсь использовать функцию, описанную выше? Вместо этого безопаснее сделать следующее? (Предположим, что программист не будет звонить бесплатно, если он знает, что линия не занята)

Вызов

char* garbageLine = line;
myFunc(line);
free(garbageLine);

Определение функции

void function myFunc(char* line) {
    // Do some work
    // Call strtok on 'line' multiple times until it returns NULL
    // Do more work
}

person user246392    schedule 15.02.2011    source источник


Ответы (5)


strtok() ничего не освобождает, так как не знает, где хранится строка. Он может быть в стеке или в куче, ему все равно! :)

Вместо этого безопаснее сделать следующее?

Ваш второй пример намного лучше, так как он упрощает myFunc() и делает его полезным в большем количестве ситуаций, поскольку функции не нужно знать, где выделена строка. Удалив вызов free() из myFunc(), вы сможете использовать эту функцию для разбора строк из стека или кучи. Вызывающий выделяет память, вызывающий освобождает память!

Дополнительная литература: strtok()

person x-x    schedule 15.02.2011
comment
Обратите внимание, что второй подход можно упростить до myFunc(line); free(line); — временное значение не нужно, так как указатель line передается по значению в myFunc(). - person caf; 15.02.2011
comment
@caf Вы, конечно, предполагаете, что OP действительно выделил строку, но это не очевидно из кода. Учитывая очевидную путаницу с распределением и освобождением памяти, предположение может быть необоснованным. - person Jim Balter; 15.02.2011

В комментарии к вашему вопросу вы говорите, что «вызываете strtok в« строке »несколько раз, пока он не вернет NULL». Это звучит так, как будто вы неправильно используете strtok. В первый раз, когда вы вызываете его, вы должны вызывать его с 'строкой' в качестве аргумента; при последующих вызовах вы должны передать его NULL. Возьмем в качестве примера следующее:

void function myFunc(char* line) {
  char *segment; // This will point at each delimited substring in turn.

  segment = strtok(line, " ");

  // Do something with segment.

  segment = strtok(NULL, " ");

  // Do something with the new segment.

  free(line);
}

Однако, как сказал DrTwox, ваш второй пример лучше — «строка» должна быть освобождена тем же контекстом, который ее выделил (или нет), поэтому вызов free() не принадлежит этой функции. И вам лучше зациклить его - что-то вроде:

void function myFunc(char* line) {
  char *segment;

  segment = strtok(line, " ");

  while (segment != NULL) {
    // Do something with segment.

    segment = strtok(NULL, " ");
  }
}

Вызов такой:

char *line = malloc(20*sizeof(char));

// Check that malloc succeeded here.
// Put some data into 'line'.

myFunc(line);

free(line);

// No 'garbageLine' required.

То, как работает strtok, немного сложно объяснить, но у вас есть важные части — он не выделяет и не освобождает память. Вместо этого он работает, изменяя строку, которую вы ему передали.

person user617357    schedule 15.02.2011
comment
+1 за упоминание о том, что strtok(LINE, "<token>") не работает, если все последующие вызовы strtok не получают NULL в качестве аргумента: strtok(NULL, "<token>"); - person Alexej Magura; 23.03.2014

Стоит пояснить, что strtok выполняет свою работу следующим образом:

  1. возврат указателей, указывающих на исходную строку; и

  2. заменяя каждый найденный символ-разделитель на NULL.

Таким образом, все на месте, и ему не нужно выделять никакой памяти.

person Eamon Egan    schedule 11.04.2019

Какое это имеет отношение к strtok()? Если вы выделяете память, вам нужно ее освободить. Где ваше приложение решает выделить и освободить память, зависит от вас. Но если вы передаете память strtok(), это не имеет значения, если и когда память будет выделена или освобождена.

person Jonathan Wood    schedule 15.02.2011
comment
Правильно ли я освобождаю память в обоих этих подходах? Все, что мне интересно, это то, что strtok неявно освобождает память. Имейте в виду, что я не эксперт в языке. - person user246392; 15.02.2011
comment
@ user246392: Как я уже сказал в своем ответе, вызов strtok() никак не влияет на выделенную память. Он не выделяет и не освобождает память. Это не имеет отношения к вопросу выделения или освобождения памяти. Часто имеет смысл освобождать память из той же процедуры, которая ее выделяет, но если это не подходит вашему приложению, все, что действительно имеет значение, это то, что она освобождается. - person Jonathan Wood; 15.02.2011
comment
@user246392 user246392 Невозможно определить, правильно ли вы освобождаете память, потому что у нас нет возможности узнать, как вы ее распределили. Однако я могу сказать, что вы почти наверняка делаете это неправильно. - person Jim Balter; 15.02.2011

strtok освобождает память не больше, чем strlen. Почему вы ожидаете этого? Какую память это освободит? Возможно, вы думаете, что strtok нужно освободить память, потому что он хранит NUL, но содержимое памяти не имеет значения. Когда вы выделяете память, распределитель отслеживает размер выделенного вами блока, и весь блок освобождается, когда вы его освобождаете.

person Jim Balter    schedule 15.02.2011