3

整数をテキストに変換するときは、通常、潜在的な結果を保持するために使用する大きなバッファーを作成します。sprintf()

char BigBuffer[50];
sprintf(BugBuffer, "%d", SomeInt);

スペース効率を高め、確実に移植できるようにしたいので、代わりに50、代替案を見つけました:
(sizeof(integer_type)*CHAR_BIT*0.302) + 3

// 0.0302 about log10(2)
#define USHORT_DECIMAL_BUFN ((size_t) (sizeof(unsigned short)*CHAR_BIT*0.302) + 3)
#define INT_DECIMAL_BUFN    ((size_t) (sizeof(int)           *CHAR_BIT*0.302) + 3)
#define INTMAX_DECIMAL_BUFN ((size_t) (sizeof(intmax_t)      *CHAR_BIT*0.302) + 3)

int main() {
    char usbuffer[USHORT_DECIMAL_BUFN];
    sprintf(usbuffer, "%hu", USHRT_MAX);
    printf("Size:%zu Len:%zu %s\n", sizeof(usbuffer), strlen(usbuffer), usbuffer);

    char ibuffer[INT_DECIMAL_BUFN];
    sprintf(ibuffer, "%d", INT_MIN);
    printf("Size:%zu Len:%zu %s\n", sizeof(ibuffer), strlen(ibuffer), ibuffer);

    char imbuffer[INTMAX_DECIMAL_BUFN];
    sprintf(imbuffer, "%" PRIdMAX, INTMAX_MIN);
    printf("Size:%zu Len:%zu %s\n", sizeof(imbuffer), strlen(imbuffer), imbuffer);
    return 0;
}

Size:7 Len:5 65535
Size:12 Len:11 -2147483648
Size:22 Len:20 -9223372036854775808

質問は次のとおりです。
1 別の方程式に問題はありますか?
2 より良い解決策は? - この代替手段は少し無駄があり、過度に複雑に見えるためです。

[回答を編集]

回答は 3 つの思慮深いアプローチを提供します

asprintf()
snprintf()

1 方程式を使用したコンパイル時の最大バッファ サイズ(sizeof(integer_type)*CHAR_BIT*0.302) + 3は、壊れたり改善されたりしませんでした。の影響は<locale.h>@paddy によって提案されたように調査され、ロケール設定は整数変換に影響しませんでし%d %x %u %iた。型が符号付きまたは符号なしであることがわかっている場合は、式がわずかに改善される可能性があることがわかりました (以下を参照)。「より保守的」に関する@paddyの注意は良いアドバイスです。

2asprintf()は本当に優れた汎用ソリューションですが、移植性はありません。多分ポストC11で?

3snprintf()は標準ですが、提供されたバッファーのサイズが小さすぎる場合の既知の一貫した実装の問題があります。これは、オーバーサイズのバッファーで呼び出してから、適切なサイズのバッファーを生成することを意味します。@jxh は、スレッド セーフなグローバル スクラッチ バッファーを提案して、適切なサイズのローカル バッファーで回答を形成しました。s(n)printf()この斬新なアプローチは検討に値しますが、元の質問は、呼び出しの前に保守的なバッファー サイズを決定することに重点を置いていました。

signed ((sizeof(integer_type)*CHAR_BIT-1)*0.302) + 3
unsigned (sizeof(integer_type)*CHAR_BIT*0.302) + 2
*28/93の代わりに使用できます*0.302

4

4 に答える 4

3

私には良さそうです。小数点以下を切り上げ、マイナス記号と null 用に 1 文字を追加し、適切な対策として 1 文字を追加しました。の機能を使わないのであれば、数値が長くなる心配はないと思います<locale.h>

私の質問は、あなたがこれらで何をしようとしているのかについてです。それらをスタック上に構築しているだけですか、それとも大量にメモリに入れていますか?

スタック上の一時配列では、キャッシュのパフォーマンスに影響を与える可能性は低いため、通常、数バイトを処理することはありません。それは確かにあなたの記憶を吹き飛ばすつもりはありません。

これらを大量に保存する予定がある場合は、プールを検討することをお勧めします。ただし、プーリングのメモリ オーバーヘッドを考慮する必要があります。プールの性質上、使用するよりも多くのメモリを予約する必要があります。また、64 ビットをコンパイルする場合、ポインターは 8 バイトです。数値のほとんどが 4 文字の長さである場合、8 バイトのポインターと各数値の 5 バイトのストレージがあれば、おそらく 64 ビットの数値を除いて、考えられる利点が無効になります。

これらは私の思考プロセスです。脂肪を上手に取り除いたように思えます。私はもう少し保守的な傾向がありますが、それはほとんどパラノイアかもしれません. シンプルに保つことが通常の方法であり、考えすぎると罠になる可能性があります. 考えすぎている場合は、その理由を検討し、実際にそれほど考える必要がある問題かどうかを判断してください。

于 2013-09-10T00:41:17.960 に答える
2

これが私の以前のコメントを拡張したスキームです。INTMAX_DECIMAL_BUFN最悪の場合のバッファ サイズとして を使用し、 での印刷に使用しますsnprintf()。によって返される値はsnprintf()、出力文字列に必要な配列サイズと正確に一致する VLA を宣言するために使用され、その文字列は VLA にコピーされます。

#define INTMAX_DECIMAL_BUFN ((size_t) (sizeof(intmax_t)*CHAR_BIT*0.302) + 3)

char numstr[INTMAX_DECIMAL_BUFN];

int main () {
    int n = snprintf(numstr, sizeof(numstr), "%hu", USHRT_MAX);
    char usbuffer[n+1];
    strcpy(usbuffer, numstr);
    printf("Size:%zu Len:%zu %s\n", sizeof(usbuffer), strlen(usbuffer), usbuffer);
}

スレッド セーフが問題になる場合は、numstr変数をスレッド ローカルにすることができます (C.11 の_Thread_local、または GCC の に似たコンパイラ固有の拡張機能を使用__thread)。

このソリューションの価値は、スタック スペースの節約が、strcpy(). より大きな整数型を使用する数値のほとんどが、実際には最大値よりもはるかに小さい値をとる場合、この手法により大幅な節約が可能になります (作成する配列の数によって異なります)。

于 2013-09-10T01:17:27.793 に答える
2

asprintf() は便利です。char ** を取り、malloc() を使用して必要なスペースを取得するため、後で free() する必要があります。

必要なスペースについて心配する必要はありません。

int asprintf(char **ret, const char *format, ...); 

char *p
asprintf(&p, "%XXXX", ...); 
:
:
free(p);
于 2013-09-10T00:12:41.417 に答える
2

これらは問題ありません。

元の関数 (*BSD では、最終的には C99 になりました) を設計しsnprintf()て、バッファーが十分に大きかった場合に出力される文字数を返しました。準拠している場合snprintf()は、印刷を 2 回行うことができます。最初に、割り当てるスペースの量を指定します ('\0'もちろん、終了のために 1 つ追加する必要があります)。%nこれには明らかな欠点が 2 つあります。書式設定を 2 回行う必要があることと、最初の呼び出しで何かが変更され (たとえば、ディレクティブによる書き込み)、2 番目の呼び出しで異なる出力が生成されるという同期の問題が発生する可能性があることです。

残念ながら、これが機能しない非準拠のsnprintf()実装があります。[編集:大きなバッファを提供するjxh's answerの使用法で機能します。失敗するケースは、必要な容量を見つけるために小さすぎるバッファを指定した場合です。]

于 2013-09-10T00:48:45.300 に答える