我想做这样的事情:
char sLength[SIZE_T_LEN];
sprintf(sLength, "%zu", strlen(sSomeString));
也就是说,我想在缓冲区中打印一个 size_t
类型的值。问题是:SIZE_T_LEN
的价值应该是多少?关键是要知道所需的最小大小,以确保永远不会发生溢出。
sizeof(size_t) * CHAR_BIT
(从limits.h
开始)给出了位数。对于十进制输出,为了安全起见,请使用每位数 3(十六进制为 4)位的(粗略)较低估计值。不要忘记为字符串的nul
终止符添加1
。
所以:
#include <limits.h>
#define SIZE_T_LEN ( (sizeof(size_t) * CHAR_BIT + 2) / 3 + 1 )
这将生成 size_t
本身类型的值。在典型的 8/16/32/64 位平台上不需要+ 2
(size_t
的最小可能大小为 16 位)。这里的误差已经足够大,可以产生正确截断的结果。
请注意,这给出了一个上限,并且由于使用了CHAR_BIT
,因此是完全可移植的。要获得确切的值,您必须使用log10(SIZE_MAX)
(请参阅JohnBollinger对此的回答)。但这会产生浮点数,并且可以在运行时计算,而上面的版本是编译时评估的(并且可能比粗略估计的成本更高)。除非您有一个非常受 RAM 约束的系统,否则没关系。在这样的系统上,无论如何您都应该避免使用stdio
。
为了绝对安全起见,您可能希望使用snprintf
(但这不是必需的)。
考虑到字符串终止符的空间,确切的答案是
log10(SIZE_MAX) + 2
其中SIZE_MAX
宏在 stdint.h
中声明。 不幸的是,这不是编译时常量(由于使用了log10()
)。 如果您需要在编译时计算的编译时常量,则可以使用以下命令:
sizeof(size_t) * CHAR_BIT * 3 / 10 + 2
这给出了至少 256 位size_t
的正确答案。 它基于这样一个事实,即 210 非常接近 1000。 在某些大量位上,它将太小一,而对于更多的位,它将进一步落后。 如果您担心如此大的size_t
那么您可以在结果中添加一个甚至两个。
如果可以使用 snprintf,请使用:
int len = snprintf (NULL, 0, "%zu", strlen (sSomeString));
在字符使用 8 位表示的系统上,
如果sizeof(size_t)
为 2,则最大值为:65535
如果sizeof(size_t)
为 4,则最大值为:4294967295
如果sizeof(size_t)
为 8,则最大值为:18446744073709551615
如果sizeof(size_t)
为 16,则最大值为:3.4028237e+38
您可以使用该信息通过预处理器提取字符串的最大大小。
示例程序:
#include <stdio.h>
#ifdef MAX_STRING_SIZE
#undef MAX_STRING_SIZE
#endif
// MAX_STRING_SIZE is 6 when sizeof(size_t) is 2
// MAX_STRING_SIZE is 11 when sizeof(size_t) is 4
// MAX_STRING_SIZE is 21 when sizeof(size_t) is 8
// MAX_STRING_SIZE is 40 when sizeof(size_t) is 16
// MAX_STRING_SIZE is -1 for all else. It will be an error to use it
// as the size of an array.
#define MAX_STRING_SIZE (sizeof(size_t) == 2 ? 6 : sizeof(size_t) == 4 ? 11 : sizeof(size_t) == 8 ? 21 : sizeof(size_t) == 16 ? 40 : -1)
int main()
{
char str[MAX_STRING_SIZE];
size_t a = 0xFFFFFFFF;
sprintf(str, "%zu", a);
printf("%sn", str);
a = 0xFFFFFFFFFFFFFFFF;
sprintf(str, "%zu", a);
printf("%sn", str);
}
输出:
4294967295
18446744073709551615
很容易使其适应使用 16 位表示字符的系统。