确定 sprintf 缓冲区大小 - 标准是什么?

发布于 2024-09-27 01:27:17 字数 196 浏览 4 评论 0原文

当像这样转换 int 时:

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

确定 a 应该有多大的最佳方法是什么?我认为手动设置它是可以的(因为我已经看到它到处使用),但它应该有多大? 32 位系统上可能的最大 int 值是多少,是否有一些棘手的方法可以动态确定该值?

When converting an int like so:

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

what's the best way to determine how large a should be? I assume manually setting it is fine (as I've seen it used everywhere), but how large should it be? What's the largest int value possible on a 32 bit system, and is there some tricky way of determining that on the fly?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

不忘初心 2024-10-04 01:27:17

这里有些人认为这种方法太过分了,对于将整数转换为字符串,我可能更倾向于同意。但是,当无法找到字符串大小的合理界限时,我看到了这种方法的使用,并且自己也使用过。

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

我将分解这里发生的事情。

  1. 在第一行,我们想要确定需要多少个字符。 snprintf 的前 2 个参数告诉它我想将结果的 0 个字符写入 NULL。当我们这样做时,snprintf 实际上不会在任何地方写入任何字符,它只会返回已写入的字符数。这就是我们想要的。
  2. 在第二行,我们动态地将内存分配给 char 指针。确保将 1 添加到所需的大小(对于尾随 \0 终止字符)。
  3. 现在已经有足够的内存分配给 char 指针,我们可以安全地使用 sprintf 将整数写入 char 指针。

当然,如果你愿意的话,你可以让它更简洁。

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

除非这是一个“快速而肮脏”的程序,否则您总是希望确保释放使用 malloc 调用的内存。这就是 C 的动态方法变得复杂的地方。但是,恕我直言,如果您不想分配巨大的 char 指针,而大多数时候您只使用其中的一小部分,那么我认为这不是一个坏方法。

Some here are arguing that this approach is overkill, and for converting ints to strings I might be more inclined to agree. But when a reasonable bound for string size cannot be found, I have seen this approach used and have used it myself.

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

I'll break down what's going on here.

  1. On the first line, we want to determine how many characters we need. The first 2 arguments to snprintf tell it that I want to write 0 characters of the result to NULL. When we do this, snprintf won't actually write any characters anywhere, it will simply return the number of characters that would have been written. This is what we wanted.
  2. On the second line, we are dynamically allocating memory to a char pointer. Make sure and add 1 to the required size (for the trailing \0 terminating character).
  3. Now that there is enough memory allocated to the char pointer, we can safely use sprintf to write the integer to the char pointer.

Of course you can make it more concise if you want.

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

Unless this is a "quick and dirty" program, you always want to make sure to free the memory you called with malloc. This is where the dynamic approach gets complicated with C. However, IMHO, if you don't want to be allocating huge char pointers when most of the time you will only be using a very small portion of them, then I don't think this is bad approach.

ヤ经典坏疍 2024-10-04 01:27:17

通过使用 vsnprintf< 可以使 Daniel Standage 的解决方案适用于任意数量的参数/a> 是 C++11/C99 中的。

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 足够大,则已写入,不算
终止空字符,或者负值(如果是)
发生编码错误。

It's possible to make Daniel Standage's solution work for any number of arguments by using vsnprintf which is in C++11/C99.

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
}

As specified in c99 standard, section 7.19.6.12 :

The vsnprintf function returns the number of characters that would
have been written had n been sufficiently large, not counting
the terminating null character, or a negative value if an
encoding error occurred.

北方的韩爷 2024-10-04 01:27:17

int 中最大可能的位数是 CHAR_BIT * sizeof(int) ,十进制数字“值”至少 3 位,因此任意 < 所需的空间上限是宽松的代码>int 是(CHAR_BIT * sizeof(int) / 3) + 3。 +3 是因为我们在除法时向下舍入这一事实,一个用于符号,一个用于 nul 终止符。

如果“在 32 位系统上”意味着您知道 int 是 32 位,那么您需要 12 个字节。 10 个数字,1 个符号,1 个 nul 终止符。

在您的具体情况下,要转换的 int 为 132,您需要 4 个字节。巴杜姆,蒂什。

如果可以在合理的范围内使用固定大小的缓冲区,则它们是更简单的选择。我毫不谦虚地认为上面的界限是合理的(对于 32 位 int 来说是 13 个字节而不是 12 个字节,对于 64 位 int 来说是 23 个字节而不是 21 个字节)。但对于困难的情况,在 C99 中,您只需调用 snprintf 来获取大小,然后调用 malloc 即可。对于这样一个简单的案例来说,这太过分了。

The max possible number of bits in an int is CHAR_BIT * sizeof(int), and a decimal digit is "worth" at least 3 bits, so a loose upper bound on the space required for an arbitrary int is (CHAR_BIT * sizeof(int) / 3) + 3. That +3 is one for the fact that we rounded down when dividing, one for the sign, one for the nul terminator.

If by "on a 32 bit system" you mean that you know int is 32 bits, then you need 12 bytes. 10 for the digits, one for the sign, one for the nul terminator.

In your specific case, where the int to be converted is 132, you need 4 bytes. Badum, tish.

Where fixed-size buffers can be used with a reasonable bound, they are the simpler option. I not-so-humbly submit that the bound above is reasonable (13 bytes instead of 12 for 32 bit int, and 23 bytes instead of 21 for 64 bit int). But for difficult cases, in C99 you could just call snprintf to get the size, then malloc that much. That's overkill for such a simple case as this.

拔了角的鹿 2024-10-04 01:27:17

我看到这个对话已经有几年了,但我在尝试寻找 MS VC++ 的答案时发现了它,其中 snprintf 不能用于查找大小。我将发布我最终找到的答案,以防它对其他人有帮助:

VC++ 有函数 _scprintf 专门用于查找所需的字符数。

I see this conversation is a couple of years old, but I found it while trying to find an answer for MS VC++ where snprintf cannot be used to find the size. I'll post the answer I finally found in case it helps anyone else:

VC++ has the function _scprintf specifically to find the number of characters needed.

烧了回忆取暖 2024-10-04 01:27:17

如果您要打印一个简单的整数,而没有其他任何内容,则有一种更简单的方法来确定输出缓冲区大小。至少在计算上更简单,代码有点迟钝:

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

log10(value) 返回以 10 为基数存储正非零值所需的位数(减一)。对于小于 1 的数字,它有点偏离轨道,所以我们指定abs(),并为零编写特殊逻辑(三元运算符,test ? truecase : falsecase)。为存储负数符号 (val < 0) 的空间添加 1,为弥补与 log10 的差值添加 1,为空终止符添加 1(总共为 2),这样您就完成了计算给定数字所需的确切存储空间量,而无需调用 snprintf() 或等效函数两次来完成工作。另外,它使用的内存通常比 INT_MAX 所需的内存要少。

但是,如果您需要非常快速地打印大量数字,请麻烦分配 INT_MAX 缓冲区,然后重复打印到该缓冲区。内存抖动越少越好。

另请注意,您实际上可能不需要 (double) 而不是 (float)。我没有费心去检查。像这样来回投射也可能是一个问题。 YMMV 关于这一切。不过对我来说效果很好。

If you're printing a simple integer, and nothing else, there's a much simpler way to determine output buffer size. At least computationally simpler, the code is a little obtuse:

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

log10(value) returns the number of digits (minus one) required to store a positive nonzero value in base 10. It goes a little off the rails for numbers less than one, so we specify abs(), and code special logic for zero (the ternary operator, test ? truecase : falsecase). Add one for the space to store the sign of a negative number (val < 0), one to make up the difference from log10, and another one for the null terminator (for a grand total of 2), and you've just calculated the exact amount of storage space required for a given number, without calling snprintf() or equivalents twice to get the job done. Plus it uses less memory, generally, than the INT_MAX will require.

If you need to print a lot of numbers very quickly, though, do bother allocating the INT_MAX buffer and then printing to that repeatedly instead. Less memory thrashing is better.

Also note that you may not actually need the (double) as opposed to a (float). I didn't bother checking. Casting back and forth like that may also be a problem. YMMV on all that. Works great for me, though.

影子是时光的心 2024-10-04 01:27:17

首先,sprintf 是魔鬼。如果有的话,请使用 snprintf,否则您将面临内存浪费和应用程序崩溃的风险。

至于缓冲区大小,它与所有其他缓冲区一样 - 尽可能小,根据需要尽可能大。就您而言,您有一个有符号整数,因此请采用尽可能大的大小,并随意添加一点安全填充。没有“标准尺寸”。

它也不依赖于您运行的系统。如果您在堆栈上定义缓冲区(如您的示例中所示),则它取决于堆栈的大小。如果您自己创建了线程,那么您自己确定了堆栈大小,因此您知道限制。如果您期望递归或深层堆栈跟踪,那么您也需要格外小心。

First off, sprintf is the devil. If anything, use snprintf, or else you risk trashing memory and crashing your app.

As for the buffer size, it's like all other buffers - as small as possible, as big as necessary. In your case, you have a signed integer, so take the largest possible size, and feel free to add a little bit of safety padding. There is no "standard size".

It's also not dependent on what system you're running on. If you define the buffer on the stack (like in your example), it depends on the size of the stack. If you created the thread yourself, then you determined the stack size yourself, so you know the limits. If you are going to expect recursion or a deep stack trace, then you need to extra careful as well.

温柔戏命师 2024-10-04 01:27:17

您担心缓冲区大小是件好事。要在代码中应用该思想,我将使用 snprintf

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

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

Its good that you are worried about buffer size. To apply that thought in code, I would use snprintf

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

or

snprintf( a, sizeof( a ), "%d", 132 );  // when a is array, not pointer
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文