Intを次のように変換する場合:
char a[256];
sprintf(a, "%d", 132);
aの大きさを決定する最良の方法は何ですか?私はそれを手動で設定するのが良いと思います(私はそれがどこでも使われているのを見たように)が、それはどれくらいの大きさですか? 32ビットシステムで可能な最大のint値は何ですか?その場でそれを決定するいくつかのトリッキーな方法がありますか?
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
を呼び出すことができます。このような単純なケースでは、それはやり過ぎです。
このアプローチはやり過ぎだと主張している人もいます。intを文字列に変換するために、私はもっと同意する傾向があるかもしれません。しかし、文字列サイズの適切な境界が見つからない場合、このアプローチが使用されているのを見て、自分で使用しました。
int size = snprintf(NULL, 0, "%d", 132);
char * a = malloc(size + 1);
sprintf(a, "%d", 132);
ここで何が起こっているのかを分析します。
snprintf
の最初の2つの引数は、結果の0文字をNULL
に書きたいことを伝えます。これを行うと、snprintf
は実際にはどこにも文字を書き込まず、単に書き込まれた文字数を返します。これが私たちが望んでいたことです。char
ポインターにメモリを動的に割り当てています。必ず、必要なサイズに1を追加します(末尾の\0
終了文字)。char
ポインターに十分なメモリが割り当てられたので、sprintf
ポインターに整数を書き込むためにchar
を安全に使用できます。もちろん、必要に応じてより簡潔にすることもできます。
char * a = malloc(snprintf(NULL, 0, "%d", 132) + 1);
sprintf(a, "%d", 132);
これが「高速で汚れた」プログラムでない限り、malloc
で呼び出したメモリを必ず解放してください。これはCで動的なアプローチが複雑になる場所です。しかし、ほとんどの場合、非常に小さな部分のみを使用するときに、巨大なchar
ポインターを割り当てたくない場合は、これは悪いアプローチではないと思います。
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が十分に大きかった場合に書き込まれたはずの文字数を返し、終端のヌル文字をカウントしません。
エンコードエラーが発生しました。
この会話は数年前ですが、snprintfを使用してサイズを見つけることができないMS VC++の答えを見つけようとしていたときに見つけました。私が最終的に見つけた答えを投稿します。
VC++の機能は _scprintf
は、特に必要な文字数を見つけるためのものです。
単純な整数を出力するだけで、他に何も出力しない場合、出力バッファサイズを決定するはるかに簡単な方法があります。少なくとも計算的には単純ですが、コードは少し鈍いです:
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。しかし、私にとってはうまくいきます。
バッファサイズが心配なのは良いことです。その考えをコードに適用するには、 snprintf を使用します
snprintf( a, 256, "%d", 132 );
または
snprintf( a, sizeof( a ), "%d", 132 ); // when a is array, not pointer
まず、sprintfは悪魔です。どちらかといえば、snprintfを使用します。そうしないと、メモリを破壊してアプリがクラッシュする危険があります。
バッファサイズに関しては、他のすべてのバッファと同じです。可能な限り小さく、必要に応じて大きくします。あなたの場合、あなたは符号付き整数を持っているので、可能な限り大きなサイズを取り、安全なパディングを少し追加してください。 「標準サイズ」はありません。
また、実行しているシステムに依存しません。 (例のように)スタック上にバッファを定義する場合、それはスタックのサイズに依存します。自分でスレッドを作成した場合は、スタックサイズを自分で決定したので、制限がわかります。再帰または深いスタックトレースが予想される場合は、さらに注意する必要があります。