分配多少 space 用于在字符串中打印 long int 值?

How much space to allocate for printing long int value in string?

我想在动态分配的字符串中存储一个 long 值(LONG_MAX 在我的测试程序中),但我很困惑我需要为要在字符串中显示的数字分配多少内存.

我的拳头尝试:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

int main(void)
{
    char *format = "Room %lu somedata\n";
    char *description = malloc(sizeof(char) * strlen(format) + 1);

    sprintf(description, format, LONG_MAX);

    puts(description);

    return 0;
}

编译为

gcc test.c

然后 运行 它(并将其输送到 hexdump):

./a.out | hd 

Returns

00000000  52 6f 6f 6d 20 39 32 32  33 33 37 32 30 33 36 38  |Room 92233720368|
00000010  35 34 37 37 35 38 30 37  20 62 6c 61 62 6c 61 0a  |54775807 blabla.|
00000020  0a                                                |.|
00000021

看看输出,我的 sizeof(char) * strlen(format) + 1 内存分配似乎是错误的(分配的内存太少)而且它更意外地工作?

那么正确的分配金额是多少?

我的下一个想法是(伪代码):

sizeof(char) * strlen(format) + strlen(LONG_MAX) + 1

这似乎太复杂了,而且很不合逻辑。还是我做错了什么?

您不能使用该格式。你需要观察者

LONG_MAX = 2147483647 = 10 characters
"Room  somedata\n" = 15 characters

添加 Null = 26 个字符

所以使用

malloc(26)

应该足够了。

好吧,如果 long 在您的机器上是 32 位的,那么 LONG_MAX 应该是 2147483647,它有 10 个字符长。您需要考虑到这一点、字符串的其余部分和空字符。

请记住,long 是一个带符号的值,最大值为 LONG_MAX,并且您使用的是 %lu(应该打印 unsigned long 值).如果您可以将带符号的值传递给此函数,则为减号添加一个额外的字符,否则您可以使用 ULONG_MAX 来更清楚地说明您的限制是什么。

如果您不确定自己 运行 在哪个架构上,您可以使用类似的东西:

// this should work for signed 32 or 64 bit values
#define NUM_CHARS ((sizeof(long) == 8) ? 21 : 11)

或者,安全起见,只需使用 21。:)

您必须分配一个等于数字 LONG_MAX 的位数的字符,即 2147483647。您必须再分配 10 个数字。

在您最喜欢的格式字符串中

  1. 房间 = 4 个字符
  2. somedata\n = 9
  3. 空格 = 2
  4. 空终止 = 1

你必须 malloc 26 个字符

如果你想确定运行时你的号码有多少位,你必须编写一个逐位测试号码的函数:

  while(n!=0)
  {
      n/=10;             /* n=n/10 */
      ++count;
  }

另一种方法是将 sprintf 结果临时存储在本地缓冲区中,并 mallocate strlen(tempStr)+1 个字符。

你完全错了。 LONG_MAX是一个整数,所以不能调用strlen()。给出最长结果的不是数字,LONG_MIN 是。因为它也打印一个减号字符。

一个不错的方法是写一个函数

char* mallocprintf (...)

与 printf 具有相同的参数,returns 是使用 malloc 分配的具有完全正确长度的字符串。如何做到这一点:首先弄清楚 va_list 是什么以及如何使用它。然后弄清楚如何使用 vsnprintf 找出 printf 的结果在没有实际打印的情况下会有多长时间。然后调用 malloc,并再次调用 vsnprintf 以生成字符串。

当您打印使用 %s 的字符串,或使用 %s 的具有较大字段长度的字符串时,这有一个很大的优势。猜猜 %999999d 打印了多少个字符。

在这里,您使用 strlen(format) 分配内存有点问题。它将根据 %lu 字典顺序 分配内存,而不是根据可以用 %lu.

打印的值的字典顺序宽度

您应该考虑 unsigned long

的最大可能值
ULONG_MAX             4294967295

字典序 10 chars.

所以,您必须为

分配space
  • 实际字符串(包含chars),加上
  • 10 chars(最大值),对于 %lu 的字典值,加上
  • 1个字符代表-符号,如果值为负数,加上
  • 1 个空终止符。

通常这是通过在堆栈上格式化为 "known" 足够大的缓冲区,然后动态分配适合格式化字符串所需的任何内容来完成的。即:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

int main(void)
{
    char buffer[1024];

    sprintf(buffer, "Room %lu somedata\n", LONG_MAX);
    char *description = malloc( strlen( buffer ) + 1 );

    strcpy( description, buffer );
    puts(description);

    return 0;
}

你可以用snprintf()算出长度而不用担心LONG_MAX的大小。

当您使用 NULL 字符串调用 snprintf 时,它将 return 如果将其写入缓冲区则需要的字节数,然后您确切知道有多少字节必填。

    char *format = "Room %lu somedata\n";

    int len = snprintf(0, 0, format, LONG_MAX); // Returns the number of 
                               //bytes that would have been required for writing.
    char *description = malloc( len+1 );

    if(!description) 
    { 
      /* error handling */
    }

    snprintf(description, len+1, format, LON_MAX);

要分配足够的空间,考虑最坏的情况

// Over approximate log10(pow(2,bit_width))
#define MAX_STR_INT(type) (sizeof(type)*CHAR_BIT/3 + 3)

char *format = "Room %lu somedata\n";
size_t n = strlen(format) + MAX_STR_INT(unsigned long) + 1;
char *description = malloc(n);
sprintf(description, format, LONG_MAX);

迂腐的代码会考虑潜在的其他语言环境

snprintf(description, n, format, LONG_MAX);

最后还是推荐2倍的buffer

char *description = malloc(n*2);
sprintf(description, format, LONG_MAX);

注意:使用说明符 "%lu" 打印,意味着 unsigned long 并在未定义的行为中传递 long LONG_MAX。建议 ULONG_MAX

sprintf(description, format, ULONG_MAX);

将预定义的常量数值转换为字符串,使用 convert digital to string in macro 中解释的宏扩展:

#define STRINGIZER_(exp)   #exp
#define STRINGIZER(exp)    STRINGIZER_(exp)

(代码由 Whozcraig 提供)。然后你可以使用

int max_digit = strlen(STRINGIZER(LONG_MAX))+1;

int max_digit = strlen(STRINGIZER(LONG_MIN));

对于有符号值,

int max_digit = strlen(STRINGIZER(ULONG_MAX));

对于无符号值。

因为 LONG_MAX 的值是 compile-time,而不是 运行-time 值,您可以确保将编译器的正确常量写入可执行文件。

使用以下代码计算保留任何正整数的十进制表示所需的字符数:

#include <math.h>

...

size_t characters_needed_decimal(long long unsigned int llu)
{
   size_t s = 1;

   if (0LL != llu)
   {
     s += log10(llu);
   }

   return s;
}

使用 C-"string" 存储数字时请注意加 1,因为 C-"string" 以 0 结尾。

这样使用:

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

size_t characters_needed_decimal(long long unsigned int);

int main(void)
{
  size_t s = characters_needed_decimal(LONG_MAX);
  ++s; /* 1+ as we want a sign */

  char * p = malloc(s + 1); /* add one for the 0-termination */
  if (NULL == p)
  {
    perror("malloc() failed");
    exit(EXIT_FAILURE);
  }

  sprintf(p, "%ld", LONG_MAX);
  printf("LONG_MAX = %s\n", p);

  sprintf(p, "%ld", LONG_MIN);
  printf("LONG_MIN = %s\n", p);

  free(p);

  return EXIT_SUCCESS;
}

归功于@Jongware 的回答,我相信最终的方法如下:

#define STRINGIZER_(exp)   #exp
#define STRINGIZER(exp)    STRINGIZER_(exp)

const size_t LENGTH = sizeof(STRINGIZER(LONG_MAX)) - 1;

字符串转换将其转换为字符串文字,因此附加了一个空终止符,即 -1。

并不是因为一切都是编译时常量,你也可以简单地将字符串声明为

const char *format = "Room " STRINGIZER(LONG_MAX) " somedata\n";

最安全:

不预测所​​需的分配,而是使用 asprintf()。此函数根据需要分配内存。

char *description = NULL;
asprintf(&description, "Room %lu somedata\n", LONG_MAX);

asprintf() 不是标准 C,但在 *nix 中很常见,其源代码可用于其他系统。

Why Use Asprintf?
apple
android