【问题标题】:behaviour of malloc(0)malloc(0) 的行为
【发布时间】:2012-05-21 11:42:41
【问题描述】:
int main()
{
    char *p;
    p = (char* ) malloc(sizeof(char) * 0);
    printf("Hello Enter the data without spaces :\n");
    scanf("%s",p);
    printf("The entered string is %s\n",p);
    //puts(p);
}

在编译并运行上面的代码时,即使我们为指针 p 分配了一个 0 字节的内存,程序也能够读取字符串。

p = (char* ) malloc(0) 语句中实际发生了什么?

【问题讨论】:

标签: c pointers


【解决方案1】:

malloc() 将返回什么是实现定义的,但使用该指针是未定义的行为。未定义的行为意味着任何事情都可能发生,从程序正常运行到崩溃,所有安全的赌注都没有。

C99 标准:

7.22.3 内存管理函数
第 1 段:

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

【讨论】:

  • Als 所写内容的一个附录 - 未定义的行为包括“它做了我认为它应该做的事情,这一次。”下一次可能会有所不同。在不同的操作系统上可能会有所不同。
  • 但请注意,在malloc 的结果上调用free总是可以的。
  • ..只要你只这样做一次。
【解决方案2】:

除了 Als 注释之外 - 会发生什么:您将某处写入内存并从那里检索数据。因此,根据您的系统和操作系统类型,您会遇到异常或只是一些未定义的行为

【讨论】:

    【解决方案3】:

    出于好奇,我在 linux 上使用 gcc 测试了您的代码,它比我预期的要健壮得多(毕竟,将数据写入长度为 0 的字符缓冲区是未定义的行为......我会 预计它会崩溃)。

    这是我对您的代码的修改:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
        char *p;
        p = malloc(sizeof(char)*0);
        printf("Hello Enter some without spaces :\n");
        scanf("%s",p);
    
        char *q;
        q = malloc(sizeof(char)*0);
        printf("Hello Enter more data without spaces :\n");
        scanf("%s",q);
    
        printf("The first string is '%s'\n",p);
        printf("The second string is '%s'\n",q);
    }
    

    我的第一个想法是,您可能会因为您只将数据读取到单个内存位置这一事实而节省 - 如果您使用两个缓冲区,第二个可能会覆盖第一个...所以我将代码分解为输入和输出部分:

    Hello Enter some without spaces :
    asdf
    Hello Enter more data without spaces :
    tutututu
    The first string is 'asdf'
    The second string is 'tutututu'
    

    如果第一个缓冲区被覆盖,我们会看到

    The first string is 'tutututu'
    The second string is 'tutututu'
    

    所以事实并非如此。 [但这取决于您将多少数据打包到每个缓冲区中...见下文]

    然后,我将大量数据粘贴到两个变量中:

    perl -e 'print "c" x 5000000 . "\n" ' | xsel -i
    

    (这会将 4+ MB 的 'c' 放入复制缓冲区)。我将其粘贴到第一个和第二个 scanf 调用中。该程序在没有分段错误的情况下使用它。

    即使我没有分段错误,第一个缓冲区确实被覆盖了。我说不出来,因为太多的数据飞到了屏幕上。这是一个数据较少的运行:

    $ ./foo
    Hello Enter some without spaces :
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    Hello Enter more data without spaces :
    ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
    The first string is 'aaaaaaaaaaaa'
    The second string is 'ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc'
    

    在 aaaaaaaaaaaa 之后有一个小字形,这就是我的终端如何表示它无法显示的 unicode 字符。这是覆盖数据的典型情况:您不知道什么会覆盖您的数据...这是未定义的行为,因此您很容易受到鼻恶魔的影响。

    底线是,当您写入尚未分配空间的内存时(显式使用 malloc 或隐式使用数组),您就是在玩火。迟早你会覆盖记忆,给自己带来各种各样的悲痛。

    真正的教训是C 不做边界检查。它会很高兴让您写入不属于您的内存。你可以整天这样做。您的程序可能会正确运行,也可能不会。它可能会崩溃,它可能会写回损坏的数据,或者它可能会工作,直到您扫描的字节比测试时使用的多一个字节。它不在乎,所以你必须这样做。

    malloc(0) 的情况只是this question 的一个特例。

    【讨论】:

    • 当我们没有为指针分配任何内存时,在这种情况下写入不存在的内存应该会导致分段错误,对吧?我也在 linux 上使用 gcc 并且没有发生分段错误。我对 malloc(0) 的这种行为感到困惑!
    • @svKris 我最好的猜测是,当你运行 malloc(0) 时,你会在堆中的某处得到一个指针。 malloc 不保留任何内存,因此您无法保证不会覆盖堆中其他地方的数据,但它(可能)不会对您产生段错误。如果您尝试写入未初始化的指针,您将写入谁知道什么内存地址,因此您可能会出现段错误。具体如何处理取决于实现。
    • @svKris 根据上面引用的 c99 标准,malloc(0) 可能返回 null,也可能返回其他内容。写入空指针会给你一个段错误,所以显然这不是 gcc 所做的。
    • 说真的,应该不要期望它会崩溃。没有定义它应该崩溃。恰恰相反。
    • @BartonChittenden 写信给NULL "will segfault" 也是一个错误的假设。它具有未定义的行为,未定义它必须崩溃。
    猜你喜欢
    • 2015-06-17
    • 1970-01-01
    • 2012-02-07
    • 2011-01-02
    • 2017-02-07
    • 2015-06-05
    • 2011-01-21
    相关资源
    最近更新 更多