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

时间:2010-10-13 00:21:56

标签: c int printf

当像这样转换int时:

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

确定 a 应该有多大的最佳方法是什么?我假设手动设置它很好(因为我看到它在任何地方使用),但它应该有多大?在32位系统上可能的最大int值是多少,是否有一些棘手的方法来确定它?

7 个答案:

答案 0 :(得分:73)

有些人认为这种方法有点过分,而且对于将int转换为字符串,我可能更倾向于同意。但是当找不到合理的字符串大小限制时,我已经看到了这种方法并且自己使用了它。

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

我会打破这里发生的事情。

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

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

    除非这是一个“快速而肮脏”的程序,否则您始终要确保释放使用malloc调用的内存。这是C动态方法变得复杂的地方。但是,恕我直言,如果你不想在大多数情况下分配巨大的char指针,你只会使用它们中的一小部分,那么我不要以为这是不好的方法。

答案 1 :(得分:14)

通过使用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 standard中第7.19.6.12节所述:

  

vsnprintf函数返回的字符数   写得已经足够大了,不算数   终止空字符,如果是,则为负值   编码错误发生。

答案 2 :(得分:12)

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个字节。 Badum,tish。

如果固定大小的缓冲区可以使用合理的边界,则它们是更简单的选项。我不是那么谦卑地提出上面的绑定是合理的(32位int为13个字节而不是12个,而64位int为23个字节而不是21个)。但对于疑难案例,在C99中你可以调用snprintf来获取大小,然后调用malloc那么多。对于这样一个简单的案例来说,这太过分了。

答案 3 :(得分:8)

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

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

答案 4 :(得分:2)

如果您正在打印一个简单的整数,而不是其他任何东西,那么确定输出缓冲区大小的方法要简单得多。至少在计算上更简单,代码有点迟钝:

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

log10(value)返回在基数10中存储正非零值所需的位数(减1)。对于小于1的数字,它稍微偏离轨道,因此我们指定abs(),并且代码特殊零逻辑(三元运算符,test?truecase:falsecase)。为空格添加一个以存储负数的符号(val <0),一个用于组成与log10的差异,另一个用于空终止符(总计为2),并且您只是计算给定数字所需的确切存储空间量,而无需调用snprintf()或等效两次来完成工作。此外,它通常使用的内存少于INT_MAX所需的内存。

但是,如果您需要非常快速地打印大量数字,请务必分配INT_MAX缓冲区然后重复打印到该缓冲区。记忆力减少更少。

另请注意,您可能实际上不需要(double)而不是(float)。我没打扰检查。像那样来回摆动也可能是个问题。 YMMV就这一切。但是对我来说很有用。

答案 5 :(得分:1)

你很担心缓冲区的大小。要在代码中应用这种想法,我会使用snprintf

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

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

答案 6 :(得分:0)

首先,sprintf是魔鬼。如果有的话,请使用snprintf,否则您就有可能破坏内存并使应用崩溃。

对于缓冲区大小,它就像所有其他缓冲区一样 - 尽可能小,尽可能大。在您的情况下,您有一个有符号整数,所以采取尽可能大的尺寸,并随意添加一点安全填充。没有“标准尺寸”。

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