web-dev-qa-db-ja.com

sprintfバッファサイズの決定-標準は何ですか?

Intを次のように変換する場合:

char a[256];
sprintf(a, "%d", 132);

aの大きさを決定する最良の方法は何ですか?私はそれを手動で設定するのが良いと思います(私はそれがどこでも使われているのを見たように)が、それはどれくらいの大きさですか? 32ビットシステムで可能な最大のint値は何ですか?その場でそれを決定するいくつかのトリッキーな方法がありますか?

42

Intの最大可能ビット数はCHAR_BIT * sizeof(int)であり、10進数は少なくとも3ビット「価値」があるため、任意のintに必要なスペースのゆるい上限は(CHAR_BIT * sizeof(int) / 3) + 3。この+3は、除算の際に切り捨てたという事実、サイン、NULターミネーターの1つです。

「32ビットシステム上」でintが32ビットであることを知っている場合は、12バイトが必要です。数字に10、符号に1つ、NULターミネータに1つ。

特定のケースでは、変換されるintは132、4バイト必要です。バドゥム、ティシュ。

固定サイズのバッファーを適切な範囲で使用できる場合、より簡単なオプションです。上記の範囲が妥当であることをそれほど謙虚に送信しません(32ビットintの場合は12バイトではなく13バイト、64ビットintの場合は21バイトではなく23バイト)。しかし、困難な場合には、C99でsnprintfを呼び出してサイズを取得し、それからmallocを呼び出すことができます。このような単純なケースでは、それはやり過ぎです。

13
Steve Jessop

このアプローチはやり過ぎだと主張している人もいます。intを文字列に変換するために、私はもっと同意する傾向があるかもしれません。しかし、文字列サイズの適切な境界が見つからない場合、このアプローチが使用されているのを見て、自分で使用しました。

int size = snprintf(NULL, 0, "%d", 132);
char * a = malloc(size + 1);
sprintf(a, "%d", 132);

ここで何が起こっているのかを分析します。

  1. 最初の行では、必要な文字数を決定します。 snprintfの最初の2つの引数は、結果の0文字をNULLに書きたいことを伝えます。これを行うと、snprintfは実際にはどこにも文字を書き込まず、単に書き込まれた文字数を返します。これが私たちが望んでいたことです。
  2. 2行目では、charポインターにメモリを動的に割り当てています。必ず、必要なサイズに1を追加します(末尾の\0終了文字)。
  3. charポインターに十分なメモリが割り当てられたので、sprintfポインターに整数を書き込むためにcharを安全に使用できます。

もちろん、必要に応じてより簡潔にすることもできます。

char * a = malloc(snprintf(NULL, 0, "%d", 132) + 1);
sprintf(a, "%d", 132);

これが「高速で汚れた」プログラムでない限り、mallocで呼び出したメモリを必ず解放してください。これはCで動的なアプローチが複雑になる場所です。しかし、ほとんどの場合、非常に小さな部分のみを使用するときに、巨大なcharポインターを割り当てたくない場合は、これは悪いアプローチではないと思います。

77
Daniel Standage

C++ 11/C99にある vsnprintf を使用して、Daniel Standageのソリューションを任意の数の引数に対して機能させることができます。

int bufferSize(const char* format, ...) {
    va_list args;
    va_start(args, format);
    int result = vsnprintf(NULL, 0, format, args);
    va_end(args);
    return result + 1; // safe byte for \0
}

c99標準 のセクション7.19.6.12で指定されているとおり:

Vsnprintf関数は、nが十分に大きかった場合に書き込まれたはずの文字数を返し、終端のヌル文字をカウントしません。
エンコードエラーが発生しました。

14
Regis Portalez

この会話は数年前ですが、snprintfを使用してサイズを見つけることができないMS VC++の答えを見つけようとしていたときに見つけました。私が最終的に見つけた答えを投稿します。

VC++の機能は _scprintf は、特に必要な文字数を見つけるためのものです。

8
HopeItHelps

単純な整数を出力するだけで、他に何も出力しない場合、出力バッファサイズを決定するはるかに簡単な方法があります。少なくとも計算的には単純ですが、コードは少し鈍いです:

char *text;
text = malloc(val ? (int)log10((double)abs(val)) + (val < 0) + 2 : 2);

log10(value)は、10を底とする正の非ゼロ値を格納するために必要な桁数(1を除く)を返します。1未満の数値ではRailsから少し外れます。 ()、およびゼロの特別なロジックをコーディングします(三項演算子、テスト?truecase:falsecase)。負の数の符号を格納するスペース(val <0)に1つを追加し、log10との差を補います。 nullターミネータ用のもう1つ(合計2)、およびsnprintf()または同等のものを呼び出すことなく、指定された数値に必要なストレージスペースの正確な量を計算しただけtwiceさらに、一般的にINT_MAXが必要とするよりも少ないメモリを使用します。

ただし、大量の数字を非常に高速に印刷する必要がある場合は、わざわざINT_MAXバッファーを割り当ててから、代わりに繰り返し印刷します。メモリスラッシングは少ない方が良いです。

また、(float)とは対照的に(double)は実際には必要ないかもしれないことに注意してください。わざわざチェックしませんでした。そのように前後にキャストすることも問題になる場合があります。すべてのYMMV。しかし、私にとってはうまくいきます。

2
Pegasus Epsilon

バッファサイズが心配なのは良いことです。その考えをコードに適用するには、 snprintf を使用します

snprintf( a, 256, "%d", 132 );

または

snprintf( a, sizeof( a ), "%d", 132 );  // when a is array, not pointer
1
Arun

まず、sprintfは悪魔です。どちらかといえば、snprintfを使用します。そうしないと、メモリを破壊してアプリがクラッシュする危険があります。

バッファサイズに関しては、他のすべてのバッファと同じです。可能な限り小さく、必要に応じて大きくします。あなたの場合、あなたは符号付き整数を持っているので、可能な限り大きなサイズを取り、安全なパディングを少し追加してください。 「標準サイズ」はありません。

また、実行しているシステムに依存しません。 (例のように)スタック上にバッファを定義する場合、それはスタックのサイズに依存します。自分でスレッドを作成した場合は、スタックサイズを自分で決定したので、制限がわかります。再帰または深いスタックトレースが予想される場合は、さらに注意する必要があります。

0
EboMike