将 unsigned long 转换为字符串的缓冲区大小

Buffer size for converting unsigned long to string

参考问题和答案 here: 我可以使用这种方法,以便解决方案与平台无关吗?

char *buff = (char*) malloc(sizeof(unsigned long)*8);
sprintf(buff, "%lu", unsigned_long_variable);

我在这里获取缓冲区长度的值,因为它类似于无符号长变量。这种做法正确吗?

The C standard doesn't put an upper limit to the number of bits per char.

如果有人构建了一个 C 编译器,例如每个字符使用 2000 位,则输出可能会溢出缓冲区。

您应该使用 limits.h 中的 CHAR_BIT 而不是 8

此外,请注意,每 3 位需要(略少于)1 个字符,并且字符串终止符需要 1 个字节。

所以,像这样:

#include <limit.h>

char *buff = malloc(1 + (sizeof(unsigned long) * CHAR_BIT + 2) / 3);
sprintf(buff, "%lu", unsigned_long_variable);

不,这不是计算缓冲区大小的正确方法。

例如对于 4 字节无符号长整型,您的值最大为 2^32-1 这意味着 10 位十进制数字。所以你的缓冲区需要 11 个字符。

您正在分配 4 * 8 = 32。

正确的公式是

ceil(log10(2^(sizeof(unsigned long) * CHAR_BIT) - 1)) + 1

(log10这里表示十进制对数)

一个好的(安全)估计是:

(sizeof(unsigned long) * CHAR_BIT + 2) / 3 + 1

因为 log10(2) 小于 0.33。

甚至尝试 计算缓冲区大小。

snprintf 开始,它会安全地告诉您需要多少个字符。然后你知道要分配多少字节来安全打印。

由于这是您不想一次又一次重复的几行代码,因此请编写一个函数 malloc_printf 来完全满足您的需求:在该函数中,调用 snprintf使用 NULL 目标,然后 malloc 缓冲区,sprintf 进入 malloc 缓冲区,然后 return 它。为了使其更快并经常避免两次 snprintfsprintf 调用,首先写入 256 个字符的缓冲区,这通常就足够了。

所以你的最终代码是

char* buff = malloc_printf ("%lu", unsigned_long_variable);

还可以使用 %s%s 格式进行快速、安全和简单的字符串连接。

您想知道需要多少个字符才能表示最大的可能 unsigned long。正确吗?

为此,您正在尝试计算最大可能 unsigned long:

sizeof(unsigned long)*8

这在几个方面是错误的。其一,sizeof returns char 的倍数,不必是 8 位。您应该乘以 CHAR_BIT(来自 <limits.h>)。但即使那样也没有必要,因为同样的 header 已经提供了最大可能的值 -- UCHAR_MAX.

那你就犯了一个错误:你的计算给出了 整数 表示 unsigned long 的大小.您想要的是字符串表示的字符的大小。这可以通过 log10() 函数(来自 <math.h>)来实现:

log10( UCHAR_MAX )

这将为您提供一个 double 值,表示 UCHAR_MAX 中的(十进制)位数。这将是一个分数,您需要将其四舍五入 (1)(ceil() 为您完成)。

因此:

#include <math.h>
#include <stdlib.h>
#include <limits.h>

int main()
{
    char * buff = malloc( ceil( log10( UCHAR_MAX ) ) + 1 );
    //...
}

总而言之,这很狡猾(我在写这篇文章时犯了 两个 错误,真丢脸——如果你在使用它时犯了错误,真丢脸) .它需要使用数学库来完成 snprintf( NULL, ... ) 可以更轻松地为您做的事情,如您链接到的问答所示。


(1): log10( 9999 ) 位数字给出 3.9999565...

简答:

#define INTEGER_STRING_SIZE(t) (sizeof (t) * CHAR_BIT / 3 + 3)

unsigned long x;
char buf[INTEGER_STRING_SIZE(x)];
int len = snprintf(buf, sizeof buf, "%lu", x);
if (len < 0 || len >= sizeof buf) Handle_UnexpectedOutput();

OP 对 sizeof(unsigned long)*8 的使用很弱。在 CHAR_BIT(每个 char 的位数)很大(必须至少为 8)的系统上,sizeof(unsigned long) 可能是 1。1*8 char对于 4294967295ULONG_MAX 的最小值)来说肯定太小了。

关注:sprintf()/snprintf() 鉴于 locale 问题,理论上,代码可能会打印额外的字符,如 4,294,967,295,从而超出预期的缓冲区。除非出现非常严格的内存限制,否则建议使用 2 倍预期大小的缓冲区。

char buf[ULONG_STRING_SIZE * 2];  // 2x
int len = snprintf(buf, sizeof buf, "%lu", x);

打印一些无符号整数的预期最大字符串宽度为ceil(log10(unsigned_MAX)) + 1。在unsigned long的情况下,ULONG_MAX的值肯定不会超过pow(2,sizeof (unsigned long) * CHAR_BIT) - 1所以代码可以使用:

#define LOG10_2 0.30102999566398119521373889472449
#define ULONG_STRING_SIZE (sizeof (unsigned long) * CHAR_BIT * LOG10_2 + 2)
// For greater portability, should use integer math.
#define ULONG_STRING_SIZE (sizeof (unsigned long) * CHAR_BIT / 3 + 2)
// or more precisely
#define ULONG_STRING_SIZE (sizeof (unsigned long) * CHAR_BIT * 28/93 + 2)

如果指定了 有符号` 整数,则使用 +3 的简短回答。