3

私はprintfと、通常のprintfを呼び出すmy_printf(...)と結果を特別な関数に送信するsprintfを書くというアイデアで遊んでいます。(ほとんどのプラットフォームでprintfと同じように動作するため、sprintfについて考えていました)。

私の考えは、これを行う小さなマクロを書くことでした:

#define my_printf(X, Y...) do{ printf(X, ## Y); \
    char* data = malloc(strlen(X)*sizeof(char)); \
    sprintf(データ, X, ## Y); \
    other_print(データ);\
    無料 (データ);}ながら (0)

しかし、sprintf は文字列を X よりもはるかに大きなサイズに拡張できるため、このメソッドはほぼ直接壊れます。

そして、数値を追加するだけで、malloc は問題を攻撃するのに間違った方法のようです.

この問題に対処する方法について、より良いアイデアを持っている人はいますか? または、sprintf の結果がどのくらい大きくなるかをどうやって知ることができますか?

ありがとうヨハン


更新: printf が印刷する文字数を返すことを忘れていました。既にマクロで printf を呼び出しているため、数値を保存する int を追加するのは非常に簡単でした。

#define buf_printf(X, Y...) do{ int len = printf(X, ## Y); \
    char* data = malloc((len+1)*sizeof(char)); \
    sprintf(データ, X, ## Y); \
    other_print(データ);\
    無料 (データ);}ながら (0)

更新:私はこれについて考えていましたが、おそらくエフェミエントが提案したものによく似た通常の関数を使用することは良い考えです. ここで鍵となるのは、さまざまな printf 関数 (vprintf、vsprintf、および vsnprintf) の v バージョンのようです。ご指摘ありがとうございます。

ありがとうヨハン

4

4 に答える 4

8

サイズを計算するには、snprintf を使用します。マニュアルページから:

" この制限のために出力が切り捨てられた場合、戻り値は文字数 (末尾の '\0' を含まない) であり、十分なスペースが利用可能であれば最終的な文字列に書き込まれます。"

snprintf は C99 からの標準です。C89 コンパイラしかない場合は、ドキュメントを確認してください。標準化前のバージョンでは、必要な値が返されない場合があります。man ページによると、バージョン 2.1 より前の glibc は、必要なサイズではなく、出力が切り詰められた場合に -1 を返していました。

ちなみに、これまでのすべての C 実装では、sizeof(char) は常に 1 と定義されています :-)

于 2009-01-31T12:02:26.893 に答える
5

これを行う最善の方法は、可変引数を使用することです。printf() と同じプロトタイプで関数を作成し、varargs 関数を使用してデータを sprintf に渡して目的のバッファーにデータを入力し、そのバッファーを printf("%s") に渡してから返します。

初期の実装の多くでは、最下位レベルの printf() 呼び出しに 4K の制限がありましたが、私はそれ以上を選択します。おそらく、上限を設定してそれに固執する必要があります。

ロギング システムで使用したトリックの 1 つは、printf() を使用してデータを /dev/null ハンドルに書き込むことでした。printf() は書き込まれた文字数を返すため、それを使用してバッファを割り当てました。しかし、printf() タイプの関数を 2 回呼び出す必要があったため、あまり効率的ではありませんでした。

于 2009-01-31T12:02:15.520 に答える
5

Linux を使用しているので、使用することをお勧めしますasprintf()- 文字列を割り当てる GNU 拡張機能です。また、C99 可変個引数マクロのおかげで、varagr をいじる必要はありません。

したがって、マクロは次のようになります。

#define MY_PRINT(...) do { \
                          char *data; \
                          asprintf(&data, __VA_ARGS__); \
                          printf("%s", data); \
                          other_print(data); \
                          free(data); \
                      } while (0)

注意!これは C99 および GNU 専用のコードです

編集:マクロ引数を一度だけ評価するようになったため、("%d", i++) のようなものでマクロを呼び出すと正しく機能します。

于 2009-02-01T08:50:09.560 に答える
2

glibc常にシステム(つまり、LinuxおよびGNUユーザーランドを備えた他のOS)で実行している場合は、asprintf同じように動作しsprintfますが、割り当て自体を自動的に処理できます。

int my_printf(const char *fmt, ...) {
    char *buf = NULL;
    int len;
    va_list ap;

    va_start(ap, &fmt);
    len = vasprintf(&buf, fmt, ap);
    va_end(ap);

    if (len < 0) {
        /* error: allocation failed */
        return len;
    }

    puts(buf);
    other_print(buf);

    free(buf);
    return len;
}

Paxの答えはより移植性がありますが、に印刷する代わりに/dev/null、より良いトリックがあります:POSIXによって、バッファとサイズをsnprintf指定でき、それが書き込んだ量を返します-しかし、明らかに実際には何も書き込まれません。NULL0

int my_printf(const char *fmt, ...) {
    char *buf;
    int len, len2;
    va_list ap;

    va_start(ap, &fmt);
    len = vsnprintf(NULL, 0, fmt, ap);
    va_end(ap);

    buf = malloc(len + 1);
    if (!buf) {
        /* error: allocation failed */
        return -1;
    }

    va_start(ap, &fmt);
    len2 = snprintf(buf, len + 1, fmt, ap);
    buf[len] = '\0';
    va_end(ap);

    /* has another thread been messing with our arguments?
       oh well, nothing we can do about it */
    assert(len == len2);

    puts(buf);
    other_printf(buf);

    free(buf);
    return len;
}

さて、onebyoneが言うように、いくつかの古いシステムは準拠していませんsnprintf。それらすべてに勝つことはできません...

于 2009-02-01T05:15:58.897 に答える