tl; drasprintf
一時的なポインタを呼び出さずに、単純に連結に使用できますか?
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はそれが私の箱にあると言います。これはバグですか?
目の前にあるmanページには
asprintf()およびvasprintf()関数は、* retを、フォーマットされた文字列を保持するのに十分な大きさのバッファーへのポインターになるように設定します。このポインタをfree(3)に渡して、割り当てられたストレージが不要になったときに解放する必要があります。十分なスペースを割り当てることができない場合、asprintf()とvasprintf()は-1を返し、retをNULLポインターに設定します。
「新しいバッファへのポインタに設定する[...]」*ret
というフレーズがないので、関数がrealloc
同じように使用していると思いがちですgetline
。
何が問題になるのでしょうか?
int asprintf(char **ret, const char *format, ...);
具体性のために署名を使用します。
asprintf
実行がrealloc
早すぎます。realloc(*ret)
元のバッファーをエイリアスしたvarargsの1つを逆参照する前に実行できるように、関数を離れた場所に実装するとします。そのバッファは解放されており、これは技術的に未定義の動作です。これはバグを表します。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;
}
しかし、それはかなり不格好です。
コンセンサス実装は、最初のコードサンプルが安全で正しいことを保証しますか?