tl; dr Можно ли наивно использовать asprintf
для конкатенации без вызова временного указателя?
Функция asprintf
, введенная GNU и принятая в нескольких других реализациях clib, является заманчивым решением для произвольной конкатенации в c с использованием такой схемы, как
int i=0;
char *str = strdup(argv[i]);
while (argv[++i]) {
asprintf(&str,"%s %s",argv[i],str); // <=== This line
}
asprintf(&str,"%s\n",str);
Когда я обернут в основной и необходимые включения, это нормально для меня.
Но...
Утечка памяти повсюду? Valgrind говорит, что это на моей коробке. Это ошибка?
На странице руководства передо мной написано:
Функции asprintf () и vasprintf () устанавливают * ret как указатель на буфер, достаточно большой для хранения форматированной строки. Этот указатель следует передать в free (3), чтобы освободить выделенную память, когда она больше не нужна. Если не удается выделить достаточно места, asprintf () и vasprintf () вернут -1 и установят в ret указатель NULL.
В отсутствие фразы "установить *ret
как указатель на новый буфер [...]" , я склонен предположить, что функция использует realloc
, как getline
.
Что может быть проблемой?
Использование подписи int asprintf(char **ret, const char *format, ...);
для конкретности.
asprintf
запускаетrealloc
слишком рано.Представьте, что мы реализуем функцию таким образом, чтобы можно было запустить
realloc(*ret)
до того, как он разыменует один из переменных, использовавших псевдоним исходного буфера. Этот буфер был освобожден, и это технически неопределенное поведение. Это было бы ошибкой.asprintf
записывает в буфер перед его чтением. В приведенном выше коде мы можем представить функцию, копирующую содержимоеargv[1]
в*ret
перед каждымva_arg
аргументаstr
. Цитируемая мной страница руководства, похоже, не исключает этого случая.asprintf
неfree *ret
ни напрямую, ни с использованиемrealloc
. Это позволит избежать проблемы номер (1), но приведет к утечке памяти при использовании, как указано выше. Опять же, эта страница руководства, похоже, не исключает этого.
Работать вокруг
Всего вышеперечисленного можно было бы избежать, заменив единственный вызов asprintf
на
{
char *newStr = NULL;
asprintf(newStr,"%s %s",argv[i],str);
free(str);
str = newStr;
}
Но это довольно неуклюже.
Гарантирует ли согласованная реализация безопасность и правильность первого образца кода?
1 ответ
Утечка памяти повсюду?
Да.
char *str = strdup(argv[i]);
Здесь str
содержит указатель на malloc()
выделенную память, которая должна быть free()
d.
asprintf(&str, "%s %s", argv[i], str);
Здесь asprintf()
изменяет str
так, чтобы он указывал на некоторую другую память, выделенную самой функцией. Теперь вы только что потеряли указатель на строку ped strdup()
, отсюда и утечка.
asprintf
использует realloc
(как, например, getline
). Из справочной страницы неясно, нужно или нет. Простое добавление "новой" работы в процитированном выше абзаце сделало бы это очень ясным, но без этого ...
asprintf()
и vasprintf()
динамически выделяют новую строку с помощью malloc(3)
». - так что, на мой взгляд, это означает, что изменение размера буфера требует вызова realloc()
.
newstr
значением NULL. А free
- это функция - для ее вызова нужны круглые скобки.
ret
является аргументом out - *ret
установлен, но его значение никогда не используется. Вы можете определить это из документации, потому что ... там сказано, что *ret
установлен, и никогда не упоминается использование его значения. Таким образом, realloc
об этом не может быть и речи.
Похожие вопросы
Новые вопросы
c
C - это язык программирования общего назначения, используемый для системного программирования (ОС и встраиваемых), библиотек, игр и кроссплатформенности. Этот тег следует использовать с общими вопросами, касающимися языка C, как это определено в стандарте ISO 9899 (последняя версия 9899: 2018, если не указано иное, а также для запросов, специфичных для версии, с c89, c99, c11 и т. Д.). C отличается от C ++ и не должен сочетаться с тэгом C ++ без разумной причины.
asprintf
- это не стандартная функция, это расширение GNU. "Это утечка памяти повсюду?" -- Конечно, это является. Док говорит освободить память, но вы этого не делаете.asprintf
, возможно, не можетrealloc
его первый аргумент - это выходной параметр, и его содержание нельзя предполагать.asprintf
, он перезаписывается вызовомmalloc
. Он не использует предварительно выделенную память.getline
подробно описано, как используется значение аргументаlineptr
. Просто сравните документацию, и очевидно, чтоasprintf
не работает таким же образом ... он никогда не использует предыдущее значение*ret
.