分配固定字符数组 vs. 指针用于强制转换为字符串

3

我刚接触C语言,试图理解以下代码为什么可以使用定长字符数组,而不能用指针:

float number = 1245.12;

// allocate a character array for the buffer
char number_as_string[50];
sprintf(number_as_string, "%f", number);

然而,分配指针(我认为会指向内存中的某个位置,并允许迭代到下一个内存点等)是行不通的:

float number = 1245.12;

// allocate a pointer
char * number_as_string;
sprintf(number_as_string, "%f", number);

1
你没有“分配指针”——你声明了一个指针变量,但是你没有为它分配任何内存来指向。 - Steve Summit
char number_as_string[50]; sprintf(number_as_string, "%f", number); 对于 number = -DBL_MAX 也会失败。我怀疑你需要 char number_as_string[DBL_MAX_10_EXP + 10]; 或者类似的东西。 - chux - Reinstate Monica
3个回答

3
我将分两部分回答你的问题。
你写道:
float number = 1245.12;
char number_as_string[50];
sprintf(number_as_string, "%f", number);

这样做是可以的,并且可以工作,但固定大小缓冲区的一个问题是很容易溢出,有时会造成灾难性的后果。因此,更好的选择是

snprintf(number_as_string, sizeof(number_as_string), "%f", number);

snprintfsprintf的一种变体,可以让您指定缓冲区的大小。这样,snprintf就能够确保不会将比缓冲区容量更多的字符写入缓冲区。

现在让我们看看如何使用snprintf来处理您的第二个示例:

char *number_as_string;
snprintf(number_as_string, ???, "%f", number);

但是我们要用多大的尺寸呢?这个第二个 number_as_string 指向的缓冲区有多大?我们不知道,这让我们意识到我们还没有为第二个 number_as_string 分配指向的缓冲区。

你需要做的是使用 malloc 来为 number_as_string 分配一些内存空间:

number_as_string = malloc(50);

现在假设malloc成功,你就知道要将什么数字作为缓冲区大小传递给snprintf了:

snprintf(number_as_string, 50, "%f", number);

现实中,我们当然会有一个变量持有已分配缓冲区的大小,而不是在多个地方重复使用数字"50"。因此,这里是更加现实的示例,包括确保malloc未失败的测试:

int buffer_size = 50;
char *number_as_string = malloc(buffer_size);
if(number_as_string == NULL) {
    fprintf(stderr, "out of memory\n");
    exit(1);
}
snprintf(number_as_string, buffer_size, "%f", number);

请务必避免以下行为:

snprintf(number_as_string, sizeof(number_as_string), "%f", number);

number_as_string是一个指针时,您将获得指针的大小(通常为4或8个字节),而不是所指向的缓冲区的大小,这正是您想要的。
我喜欢用小盒子和箭头图来理解内存分配和指针。这里是char number_as_string[50]
                  +--+--+--+--+--+--+--+--+--+--+--+--+--+   +--+--+
number_as_string: |  |  |  |  |  |  |  |  |  |  |  |  |  |...|  |  |
                  +--+--+--+--+--+--+--+--+--+--+--+--+--+   +--+--+

以下是 char *number_as_string 和成功的 number_as_string = malloc(50)

                  +-------+
number_as_string: |   *   |
                  +---|---+
                      |
                      v
                     +--+--+--+--+--+--+--+--+--+--+--+--+--+   +--+--+
                     |  |  |  |  |  |  |  |  |  |  |  |  |  |...|  |  |
                     +--+--+--+--+--+--+--+--+--+--+--+--+--+   +--+--+

但是这里只有char *number_as_string:

                  +-------+
number_as_string: |   *--------> ???
                  +-------+

箭头 - 指针 - 指向“无处”。

非常感谢,这对理解事物非常有帮助。感谢您的回答(希望对其他初学者也有帮助)。 - user11954200
如果number_as_string是一个全局指针(这意味着它被初始化为零),那么它应该指向8)X(我对骷髅和十字骨的可怕印象)。 - S.S. Anne
@JL2210 我们有Unicode,谁还需要ASCII艺术呢? →☠ - Steve Summit
好的。让我们在最后一个 ??? 的位置进行编辑,并将 "nowhere" 替换为 "to death"。 - S.S. Anne

2

指针变量需要指向某个东西,在您的第二个示例中,number_as_string 没有指向任何内容。尝试通过调用 sprintf 对其进行解引用会引发未定义行为

您需要动态分配空间:

最初的回答
char * number_as_string = malloc(50);

或者将其指向有效的位置:

最初的回答
char str[50];
char * number_as_string = str;

0

在第一种情况下

char number_as_string[50];
sprintf(number_as_string, "%f", 123.456);

你有一个自动存储变量,它是一个包含50个char的数组。这个数组通常在堆栈上,所以当你调用sprintf()时,你正在“打印”有效的内存。

在第二种情况下

char * number_as_string;
sprintf(number_as_string, "%f", 123.456);

你也有一个自动变量。但是这个变量只是一个指针。你没有向编译器请求一个数组。此外,你没有初始化指针,因此你甚至无法确定 sprintf() 是否会接收属于你的程序的内存位置。

这就是你要找的东西:

char * number_as_string = malloc(50);
sprintf(number_as_string, "%f", 123.456);

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