在字符串中打印long int值分配多少空间?

时间:2015-05-22 13:37:01

标签: c dynamic-memory-allocation

我想在动态分配的字符串中存储一个长值(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 

返回

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

这看起来太复杂,非常不具体。或者我做错了什么?

12 个答案:

答案 0 :(得分:3)

你这样做完全错了。 LONG_MAX是一个整数,因此您无法调用strlen()。并且它不是给出最长结果的数字,LONG_MIN是。因为它也会打印一个减号字符。

一个很好的方法是编写一个函数

char* mallocprintf (...)

与printf具有相同的参数,并返回使用具有完全正确长度的malloc分配的字符串。如何执行此操作:首先弄清楚va_list是什么以及如何使用它。然后弄清楚如何使用vsnprintf来找出printf的结果有多长没有实际打印。然后调用malloc,再次调用vsnprintf生成字符串。

这有一个很大的优势,它可以在您使用%s打印字符串时使用,或者使用%s使用一些大字段长度的字符串。猜猜%999999d打印了多少个字符。

答案 1 :(得分:3)

您可以使用snprintf()计算长度,而无需担心LONG_MAX的大小。

当你使用NULL字符串调用snprintf时,如果它被写入缓冲区然后你确切地知道需要多少字节,它将返回一些所需的字节数。

    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);

答案 2 :(得分:3)

使用宏扩展将预定义的常量数值转换为字符串,如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的值是编译时,而不是运行时值,因此可以确保为编译器写入正确的常量进入可执行文件。

答案 3 :(得分:1)

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

// 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);

但最后,建议使用2x缓冲区

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

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

sprintf(description, format, ULONG_MAX);

答案 4 :(得分:1)

@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";

答案 5 :(得分:0)

您无法使用该格式。你需要观察者

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

添加Null = 26个字符

所以使用

malloc(26)

应该足够了。

答案 6 :(得分:0)

好吧,如果您的计算机上的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.:)

答案 7 :(得分:0)

你必须分配一个数字等于数字LONG_MAX的数字2147483647.你必须再分配10位数。

在你喜欢的格式字符串中

  1. 房间= 4个字符
  2. somedata \ n = 9
  3. spaces = 2
  4. null termination = 1
  5. 你必须使用malloc 26 chars

    如果你想确定运行时人数如何编号,你必须编写一个数字来测试数字的函数:

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

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

答案 8 :(得分:0)

在这里,使用strlen(format)来分配内存有点问题。考虑到%lu 字典,它将分配内存,而不是基于可以使用%lu打印的值的词典宽度。

您应该考虑unsigned long

的最大可能值
ULONG_MAX             4294967295

按字典顺序10 char s。

所以,你要为

分配空间
  • 实际字符串(包含char s),加上
  • 10 char s(最多),%lu的词典值,加上
  • 1个字符来代替-符号,如果值为负,加​​上
  • 1 null终止符。

答案 9 :(得分:0)

通常这是通过格式化为&#34;已知&#34;堆栈上有足够大的缓冲区,然后动态分配适合格式化字符串所需的任何内容。即:

#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;
}

答案 10 :(得分:0)

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

#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;
}

答案 11 :(得分:0)

最安全:

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

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

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

Why Use Asprintf?
apple
android