【问题标题】:How does creating a dynamically allocated string in C work?如何在 C 中创建动态分配的字符串?
【发布时间】:2013-08-15 22:25:12
【问题描述】:

我不明白 C 中动态分配的字符串是如何工作的。下面,我有一个例子,我认为我已经创建了一个指向字符串的指针并为其分配了 0 内存,但我仍然可以给它字符。我显然做错了什么,但是什么?

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

int main(int argc, char *argv[])
{
char *str = malloc(0);
int i;
str[i++] = 'a';
str[i++] = 'b';
str[i++] = '\0';
    printf("%s\n", str);

    return 0;
}

【问题讨论】:

    标签: c string dynamic-allocation


    【解决方案1】:

    您正在做的是未定义的行为。它现在可能看起来可以工作,但不是必须工作,如果有任何变化可能会中断。

    malloc 通常会返回您可以使用的给定大小的内存块。在您的情况下,碰巧在您正在触摸的那个块之外有有效的内存。那个记忆不应该被触动; malloc 可能会将该内存用于内部管理,它可能会将该内存作为某些malloc 调用的结果,或者完全是其他东西。不管它是什么,它都不是你的,触摸它会产生不确定的行为。

    【讨论】:

    • 谢谢!有什么方法可以检查我是否为字符串分配了正确的内存量?
    • @dyxh:我不完全确定你在问什么,但如果你问是否有任何工具可以调试这样的问题,那么是的:在我的脑海中,有 Valgrind 和 AddressSanitizer。如果您询问如何正确分配内存,则需要传递 malloc 字符串的大小,包括尾随的 NUL 字节。如果您询问是否可以在代码中检查内存块是否足够大,则没有标准方法;一些实现可能会提供这样的功能,而另一些可能不会;不需要此类功能。
    【解决方案2】:

    当前 C 标准的第 7.20.3 节部分说明:

    "如果请求的空间大小为零,则行为是 实现定义:要么返回一个空指针,要么 行为就好像大小是某个非零值,除了 返回的指针不得用于访问对象。”

    这将由实现定义。它要么可以发送一个 NULL 指针,要么可以发送一个不能被引用的东西

    【讨论】:

      【解决方案3】:

      您正在覆盖未分配的内存。这可能看起来像工作。但是当你调用free 时你遇到了麻烦,堆函数试图将内存块归还。

      每个malloc() 返回的内存块都有一个头和一个尾。这些结构至少保存已分配内存的大小。有时你有额外的警卫。您正在覆盖此堆内部结构。这就是free()会抱怨或崩溃的原因。

      所以你有一个未定义的行为。

      【讨论】:

        【解决方案4】:

        通过执行malloc(0),您正在创建一个NULL 指针或可以传递给free 的唯一指针。那条线没有错。当您执行指针算术并将值分配给尚未分配的内存时,问题就出现了。因此:

        str[i++] = 'a'; // Invalid (undefined).
        str[i++] = 'b'; // Invalid (undefined).
        str[i++] = '\0'; // Invalid (undefined).
        
        printf("%s\n", str); // Valid, (undefined).
        

        做两件事总是好的:

        1. 不要malloc 0 字节。
        2. 检查以确保您malloced 的内存块有效。

        ...要检查从malloc 请求的内存块是否有效,请执行以下操作:

        if ( str == NULL ) exit( EXIT_FAILURE );
        

        ...在您致电malloc之后。

        【讨论】:

          【解决方案5】:

          您的 malloc(0) 错误。正如其他人指出的那样,最终可能会或可能不会分配一点内存,但无论 malloc 实际上对 0 做了什么,在这个简单的示例中,您都应该分配至少 3*sizeof(char) 字节的内存。

          所以这里我们有一个正确的麻烦。假设您为字符串分配了 20 个字节,然后用 19 个字符和一个 null 填充它,从而填充了内存。到目前为止,一切都很好。但是,请考虑您想在字符串中添加更多字符的情况;你不能把它们放在适当的位置,因为你只分配了 20 个字节并且你已经使用了它们。您所能做的就是分配一个全新的缓冲区(例如 40 个字节),将原来的 19 个字符复制到其中,然后在末尾添加新字符,然后释放原来的 20 个字节。听起来效率低下不是吗。而且它效率低下,分配内存需要大量工作,而且与其他语言(例如 C++)相比,您只需连接 str1 + str2 字符串。

          除了在底层,这些语言必须做完全相同的事情,即分配更多内存和复制现有数据。如果有人关心高性能,那么 C 会让您更清楚地花时间在哪里,而 C++、Java、C# 之类的语言则将昂贵的操作隐藏在易于使用的类后面。这些类可能非常聪明(例如,分配比严格必要的更多的内存以防万一),但如果您有兴趣从硬件中提取最佳性能,则必须做好准备。

          这类问题是 Facebook 和 Twitter 等运营商在发展服务方面遇到困难的原因所在。那些方便但低效的类方法迟早会成为不可持续的东西。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-10-09
            • 1970-01-01
            • 1970-01-01
            • 2015-03-09
            • 1970-01-01
            • 1970-01-01
            • 2017-06-14
            • 1970-01-01
            相关资源
            最近更新 更多