确定sprintf缓冲区大小 – 标准是什么?
当像这样转换int时:
char a[256]; sprintf(a, "%d", 132);
确定a应该多大的最好方法是什么? 我假设手动设置它是好的(因为我已经看到它在各地使用),但它应该是多大? 在32位系统上可能的最大int值是多less,并且有一些决定在飞行中的棘手方法?
int中的最大位数是CHAR_BIT * sizeof(int)
,而十进制数字是“值”至less3位,所以任意int
所需空间的松散上限是(CHAR_BIT * sizeof(int) / 3) + 3
。 这个+3就是我们在分割时舍去的事实,一个是符号,一个是终止符。
如果“在32位系统上”的意思是你知道int
是32位,那么你需要12个字节。 10位为数字,一位为符号,一位为nul终止符。
在你的具体情况下,要转换的int是132
,你需要4个字节。 Badum,tish。
在固定大小的缓冲区可以使用一个合理的界限,他们是更简单的select。 我不谦虚地提交上面的界限是合理的(对于32位int
,13字节而不是12,对于64位int
,23字节而不是21)。 但对于困难的情况下,在C99中,您可以调用snprintf
来获取大小,然后malloc
那么多。 这对于这样一个简单的例子来说太过分了。
这里有一些人认为这种做法过于矫枉过正,为了把string转换成string,我可能更倾向于同意。 但是,当找不到合理的string大小的限制时,我已经看到了这种方法,并且自己使用了它。
int size = snprintf(NULL, 0, "%d", 132); char * a = malloc(size + 1); sprintf(a, "%d", 132);
我会分解这里发生的事情。
- 在第一行,我们要确定我们需要多less个字符。
snprintf
的前两个参数告诉它,我想把结果的0个字符写成NULL
。 当我们这样做时,snprintf
实际上不会在任何地方写任何字符,它只会返回已经写入的字符数。 这是我们想要的。 - 在第二行,我们dynamic地将内存分配给一个
char
指针。 确保并添加1到所需的大小(对于尾随\0
终止字符)。 - 既然有足够的内存分配给
char
指针,我们可以安全地使用sprintf
将整数写入char
指针。
当然,如果你愿意,你可以更简洁一些。
char * a = malloc(snprintf(NULL, 0, "%d", 132) + 1); sprintf(a, "%d", 132);
除非这是一个“快速和肮脏的”程序,否则你总是要确保释放你用malloc
调用的malloc
。 这是C的dynamic方法变得复杂的地方。但是,恕我直言,如果你不想在大多数情况下分配巨大的char
指针,你将只使用它们的一小部分,那么我不认为这是不好的方法。
通过使用C ++ 11 / C99中的vsnprintf ,可以使Daniel Standage的解决scheme适用于任意数量的参数。
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函数返回已经被写入的字符数足够大,不包括终止空字符,或者如果
发生编码错误。
我看到这个对话已经有几年了,但是我在findMS VC ++的答案的时候发现了这个问题,其中snprintf不能用于查找大小。 我会发布我终于find的答案,以防别人帮助其他人:
VC ++具有_scprintf
函数_scprintf
专门查找所需的字符数。
如果你正在打印一个简单的整数,而没有别的,那么确定输出缓冲区大小的方法就简单多了。 至less在计算上更简单,代码是一点点钝的:
char *text; text = malloc(val ? (int)log10((double)abs(val)) + (val < 0) + 2 : 2);
log10(value)返回以10为底的正数非零值所需要的位数(减1)。对于小于1的数字,它会离开导轨一点,所以我们指定abs(),并将特殊逻辑代码设置为零(三元运算符,testing?truecase:falsecase)。 添加一个用于存储负数(val <0)的符号的空间,一个用于填充与log10不同的值,另一个用于空终止符(总计为2),并且您已经计算了给定数字所需的确切存储空间量,而不用调用snprintf()或等价物两次以完成工作。 另外,它通常比INT_MAX需要的内存less。
如果您需要快速打印大量数字,请尽量分配INT_MAX缓冲区,然后重复打印。 内存抖动越less越好。
另外请注意,您可能实际上并不需要(double)而不是(float)。 我没有麻烦检查。 像这样来回转换也可能是一个问题。 YMMV。 虽然对我很好。
它很好,你担心缓冲区大小。 要在代码中应用这个想法,我会使用snprintf
snprintf( a, 256, "%d", 132 );
要么
snprintf( a, sizeof( a ), "%d", 132 ); // when a is array, not pointer
首先,sprintf是魔鬼。 如果有的话,使用snprintf,否则你冒着摧毁内存和崩溃你的应用程序的风险。
至于缓冲区大小,就像所有其他缓冲区一样 – 尽可能小,尽可能大。 在你的情况下,你有一个有符号的整数,所以采取尽可能大的大小,并随意添加一点点的安全填充。 没有“标准尺寸”。
这也不依赖于你正在运行的系统。 如果你在堆栈上定义缓冲区(就像你的例子),这取决于堆栈的大小。 如果你自己创build了线程,那么你自己确定了堆栈的大小,所以你知道这个限制。 如果您期待recursion或深层堆栈跟踪,那么您还需要特别小心。