Как правильно использовать 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);
}

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

ссылка

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
}

5 ответов


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

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

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

Читать далее: strtok ()


в комментарии в вашем вопросе вы говорите, что вы"вызываете strtok on 'line' несколько раз, пока он не вернет NULL". Это звучит так, как будто вы неправильно используете strtok. В первый раз вы вызываете его, вы должны вызвать его с "line" в качестве аргумента; при последующих вызовах вы должны передать его 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, хотя ваш второй пример лучше - "линия" должна быть освобождена тем же контекстом, что и malloced (или нет), поэтому вызов 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, немного сложно объяснить, но у вас есть важные части - он не выделяет и не освобождает память. Вместо этого он работает, изменяя строку, которую вы передали ему.


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


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


пока вы используете только тип " char*", будет невозможно сделать любую манипуляцию строкой 100% безопасно из-за переполнения буфера.

что касается утечек памяти; если вы выделяете его, рекомендуется удалить его в том же блоке (если это возможно). Это означает, что если вы предоставляете реализацию strtok, она должна удалить любую память, которую она выделяет внутри, но не удалять любую память, переданную методу.

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

чтобы сделать" безопасную " манипуляцию строки переполнения без буфера, вам также нужно иметь значение maxium count. Например, функції strncpy(...) принимает параметр "максимальное количество символов". Это предотвращает некоторые виды атаки, где память" вниз по течению " выделения памяти строки может быть заполнена данными, которые позже интерпретируются как код какой-либо другой частью программы.

это означает, что ваш myFunc(char* line) должна быть подпись myfunc(char* line, int max_chars) и все ваши последующие операции должны быть гарантированы max_Chars + 1 объем памяти для работы. Плюс один должен содержать завершающий null, который может отсутствовать. Внутри вы должны убедиться, что все операции, такие как strcpy, работают только на первом max_chars (strncpy делает это).

это означает, что вы в конечном итоге всегда будете поддерживать версию "n" (количество символов) строкового манипулятора для безопасности переполнения буфера. Соедините это с сильной практикой "освобождения в блоке, который вы выделяете", и вы избежите 90% большинства ошибок программирования, связанных со строками.

иногда вы захотите сохранить выделенную строку позже (возможно, на карте). С такими случаями вам нужно позаботиться о том, чтобы очистить карту перед выходом из программы, или, по крайней мере, принять некоторые другие меры, чтобы убедиться, что карта не держит неиспользуемую память дольше, чем это необходимо.