分配多少 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 个数字。
在您最喜欢的格式字符串中
- 房间 = 4 个字符
- somedata\n = 9
- 空格 = 2
- 空终止 = 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
char
s.
所以,您必须为
分配space
- 实际字符串(包含
char
s),加上
- 10
char
s(最大值),对于 %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 中很常见,其源代码可用于其他系统。
我想在动态分配的字符串中存储一个 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 个数字。
在您最喜欢的格式字符串中
- 房间 = 4 个字符
- somedata\n = 9
- 空格 = 2
- 空终止 = 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
char
s.
所以,您必须为
分配space- 实际字符串(包含
char
s),加上 - 10
char
s(最大值),对于%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 中很常见,其源代码可用于其他系统。