6

tl; drasprintf一時的なポインタを呼び出さずに、単純に連結に使用できますか?


asprintfGNUによって導入され、他のいくつかの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, ...);具体性のために署名を使用します。

  1. asprintf実行がrealloc早すぎます。

    realloc(*ret)元のバッファーをエイリアスしたvarargsの1つを逆参照する前に実行できるように、関数を離れた場所に実装するとします。そのバッファは解放されており、これは技術的に未定義の動作です。これはバグを表します。

  2. asprintfバッファを読み取る前に、バッファに書き込みます。上記のコードでは、引数のすべての前にの内容argv[1]をコピーする関数をイメージできます。私が引用するマンページは、このケースを除外していないようです。*retva_argstr

  3. asprintffree *ret直接またはを使用してではありませんrealloc。これにより、問題番号(1)は回避されますが、上記のように使用するとメモリがリークします。繰り返しますが、そのマニュアルページはそれを除外していないようです。

回避策

asprintf上記のすべては、への単一の呼び出しをに置き換えることで回避できます。

{
  char *newStr = NULL;
  asprintf(newStr,"%s %s",argv[i],str);
  free(str);
  str = newStr;
}

しかし、それはかなり不格好です。

コンセンサス実装は、最初のコードサンプルが安全で正しいことを保証しますか?

4

1 に答える 1

3

あちこちでメモリリークが発生していますか?

はい、そうです。

char *str = strdup(argv[i]);

ここには、 dであるはずの古いメモリstrへのポインタが含まれています。malloc()free()

asprintf(&str, "%s %s", argv[i], str);

ここでは、関数自体によって割り当てられた他のメモリを指すようにasprintf()変更します。これで、 ped文字列strへのポインタが失われたため、リークが発生しました。strdup()

于 2013-03-26T18:51:04.957 に答える