如何将无符号长整型转换为字符串?

27
在C语言中,我该如何将无符号长整型(unsigned long)的值转换为字符串(char *),并保持我的源代码可移植性,或者只需重新编译使其在其他平台上工作(而无需重写代码)?
例如,如果我有sprintf(buffer, format, value),我该如何以与平台无关的方式确定缓冲区的大小?

使用sprintf,如何以平台无关的方式确定缓冲区的大小。 - Walidix
@Walidix 答案可能是 limits.h:http://en.wikipedia.org/wiki/Limits.h - Andrei Ciobanu
1
这实际上是sprintf的一个很大的弱点,在C++中通过使用流而不是缓冲区部分修复。通常的“解决方案”是分配一个非常慷慨的缓冲区,不太可能溢出,让sprintf输出到该缓冲区,然后使用strlen确定实际生成的字符串长度,calloc一个(该大小+1)的缓冲区并将字符串复制到其中。 - Carl Smotricz
1
@Walidix:你需要计算log10(ULONG_MAX)... - kennytm
如果您有snprintf,那么它是一个绝佳的答案。请查看我的回答以获取更多详细信息。 - Carl Smotricz
7个回答

36
const int n = snprintf(NULL, 0, "%lu", ulong_value);
assert(n > 0);
char buf[n+1];
int c = snprintf(buf, n+1, "%lu", ulong_value);
assert(buf[n] == '\0');
assert(c == n);

1
请注意,使用VLA(char buf[n+1];)需要C99。 - kennytm
2014年,我仍然被迫在工作中使用Visual Studio进行Windows开发,这让我很困扰,因为它不允许这样做 :( - Ruairi O'Brien
3
非常好的回答,可以用来反驳那些认为除了C++之外很难实现的人。我在看着你,Carl Smotricz! - FizzixNerd

7
标准方法是使用sprintf(buffer, "%lu", value);value的字符串表示写入buffer中。然而,溢出是一个潜在的问题,因为sprintf会快乐地(且不知情地)写入你的缓冲区末尾。
这实际上是sprintf的一个很大的弱点,在C++中通过使用流而不是缓冲区部分修复。通常的“答案”是分配一个非常慷慨的缓冲区,不太可能溢出,让sprintf输出到那个缓冲区,然后使用strlen确定实际产生的字符串长度,calloc一个(该大小+1)的缓冲区并将字符串复制到其中。 这个网站详细讨论了这个和相关的问题。
一些库提供snprintf作为替代方案,它允许您指定最大缓冲区大小。

是的,我会使用 snprintf。它是标准的,并且提供了完全解决此问题的支持。 - Johannes Schaub - litb
与您的 Gravatar 相矛盾,为理智点赞。 - Tim Post
打印数字是一种情况,您不需要使用 snprintf;您可以轻松地限制所需缓冲区的大小。(另一方面,使用 %s 进行打印则需要谨慎。) - Donal Fellows
许多程序员会更愿意使用snprintf指定非常明确的上限,而不是考虑数字大小来数学地建立这样的上限,更糟糕的是,在我看来,他们会用对数来预先计算那个大小。 - Carl Smotricz

5
你可以编写一个函数,将无符号长整型转换为字符串,类似于ltostr库函数。
char *ultostr(unsigned long value, char *ptr, int base)
{
  unsigned long t = 0, res = 0;
  unsigned long tmp = value;
  int count = 0;

  if (NULL == ptr)
  {
    return NULL;
  }

  if (tmp == 0)
  {
    count++;
  }

  while(tmp > 0)
  {
    tmp = tmp/base;
    count++;
  }

  ptr += count;

  *ptr = '\0';

  do
  {
    res = value - base * (t = value / base);
    if (res < 10)
    {
      * -- ptr = '0' + res;
    }
    else if ((res >= 10) && (res < 16))
    {
        * --ptr = 'A' - 10 + res;
    }
  } while ((value = t) != 0);

  return(ptr);
}

您可以参考我的博客这里,其中有实现和示例的解释。

3
char buffer [50];

unsigned long a = 5;

int n=sprintf (buffer, "%lu", a);

2

尝试使用sprintf函数:

unsigned long x=1000000;
char buffer[21];
sprintf(buffer,"%lu", x);

编辑:

请注意,您需要提前分配一个缓冲区,并且不知道在这样做时数字实际上会有多长。 我假设是32位的long,可以产生10位数的数字。

请查看Carl Smotricz的答案,以获得有关涉及问题的更好解释。


1

对于长整型数值,您需要添加长度信息“l”和无符号十进制整数“u”,

有关可用选项的参考,请参见sprintf

#include <stdio.h>

    int main ()
    {
      unsigned long lval = 123;
      char buffer [50];
      sprintf (buffer, "%lu" , lval );
     }

0
在编程中,如何以平台无关的方式确定缓冲区的大小?
将无符号长整型转换为字符串的挑战之一是如何确定所需的字符串大小。
动态地,反复将该值除以10,直到得到0,可找到size_needed。
value_copy = value;
unsigned size_needed = 1; // For the null character.
if (value_copy < 0) size_needed++; // Only needed for signed types.
do {
  size_needed++; // Add 1 per digit.
  value_copy /= 10;
} while (value_copy != 0);
  • 最坏情况

查找ULONG_MAX的字符串长度。
从巧妙的IMAX_BITS(m)开始,它返回类似于ULONG_MAXMersenne Number中位数的位数。(即使类型有填充,这也给我们最大的位宽。)然后乘以log102(0.301...)以找到十进制数字的数量,并加上2进行四舍五入和空字符。

#define IMAX_BITS(m) ((m)/((m)%255+1) / 255%255*8 + 7-86/((m)%255+12))
#define LOG2_10_N  28
#define LOG2_10_D  93
#define UNSIGNED_LONG_STRING_SIZE (IMAX_BITS(ULONG_MAX)*LOG2_10_N/LOG2_10_D + 2)
// Slightly different for signed types, one more for the sign:
#define SIGNED_LONG_STRING_SIZE   (IMAX_BITS( LONG_MAX)*LOG2_10_N/LOG2_10_D + 3)

有了字符串大小,就有许多可能的下一步。我喜欢使用C99(及更高版本)的复合字面量来形成所需的空间。该空间在块的末尾之前都是有效的。

char *unsigned_long_to_string(char *dest, unsigned long x) {
  sprintf(dest, "%lu", x);
  return dest;
}

// Compound literal                                        v-----------------------------------v
#define UNSIGNED_LONG_TO_STRING(u) unsigned_long_to_string((char [UNSIGNED_LONG_STRING_SIZE]){0}, (u))

int main(void) {
  puts(UNSIGNED_LONG_TO_STRING(42));
  puts(UNSIGNED_LONG_TO_STRING(ULONG_MAX));
}

输出

42
18446744073709551615  // This varies

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接