【问题标题】:How come I can initialize a pointer that has zero bytes allocated to it? [duplicate]为什么我可以初始化一个分配给它的字节为零的指针? [复制]
【发布时间】:2019-01-28 02:30:20
【问题描述】:

我知道malloc(size_t size) 分配 size 字节并返回一个指向已分配内存的指针。 .

那么为什么当我分配零字节给整数指针 p 时,我仍然能够初始化它呢?

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

int main()
{

        int *p = malloc(0);
        *p = 10;

        printf("Pointer address is: %p\n", p);
        printf("The value of the pointer: %d\n", *p);

        return 0;
}

这是我的程序输出,我期待一个分段错误。

Pointer address is: 0x1ebd260
The value of the pointer: 10

【问题讨论】:

    标签: c pointers memory malloc


    【解决方案1】:

    malloc(0) 的行为是实现定义的,它将返回一个指针或NULL。根据 C 标准,当请求零大小空间1) 时,不应使用 malloc 返回的指针。

    取消引用malloc(0) 返回的指针是未定义的行为,其中包括程序可能执行不正确(崩溃或静默生成错误结果),或者它可能恰好按照程序员的意图执行。


    1) 来自 C 标准#7.22.3p1 [强调添加]

    1 连续调用aligned_alloc、calloc、malloc 和realloc 函数分配的存储顺序和连续性未指定。如果分配成功,则返回的指针经过适当对齐,以便可以将它分配给指向具有基本对齐要求的任何类型对象的指针,然后用于访问分配空间中的此类对象或此类对象的数组(直到空间被显式释放)。已分配对象的生命周期从分配一直延伸到解除分配。每个这样的分配都应产生一个指向与任何其他对象不相交的对象的指针。返回的指针指向分配空间的开始(最低字节地址)。如果无法分配空间,则返回空指针。 如果请求的空间大小为零,则行为是实现定义的:要么返回空指针,要么行为就好像大小是某个非零值,但返回的指针不应用于访问一个对象。

    【讨论】:

      【解决方案2】:

      当您调用malloc(0) 并写入返回的缓冲区时,您调用了undefined behavior。这意味着您无法预测程序的行为方式。它可能会崩溃,它可能会输出奇怪的结果,或者(如在本例中)它可能看起来工作正常。

      仅仅因为程序可能崩溃并不意味着它

      【讨论】:

        【解决方案3】:

        一般来说,C 不会阻止你做不正确的事情。

        int *p = malloc(0); 之后,p 有一定的价值。它可能是一个空指针,也可能指向一个或多个内存字节。无论哪种情况,您都不应该使用它。1但是 C 语言不会阻止您这样做。

        当你执行*p = 10;时,编译器可能已经生成代码将10写入p指向的位置。 p 可能指向实际的可写内存,因此存储指令可以执行而不会失败。然后你把 10 写到了你不应该写到的内存位置。在这一点上,C 标准不再规定程序的行为是什么——通过写入不适当的地方,您已经破坏了 C 工作方式的模型。

        您的编译器也有可能在这种情况下识别出*p = 10; 是不正确的代码,并生成除上述写入内存之外的其他内容。一个好的编译器可能会针对此代码向您发出警告消息,但 C 标准并没有强制编译器这样做,它可能允许您的程序以其他方式中断。

        脚注

        1 如果malloc 返回一个空指针,则不应写入*p,因为它没有指向对象。如果它返回其他内容,则不应写入 *p,因为 C 2018 7.22.3.1 说,对于 malloc(0),“返回的指针不应用于访问对象。”

        【讨论】:

          【解决方案4】:

          我认为查看分段错误定义是个好主意https://en.wikipedia.org/wiki/Segmentation_fault

          以下是导致分段错误的一些典型原因:

          • 试图访问一个不存在的内存地址(在进程的地址空间之外)
          • 试图访问程序无权访问的内存(例如进程上下文中的内核结构)
          • 试图写入只读内存(如代码段)

          所以一般来说,访问程序数据段中的任何地址都不会导致段错误。这种方法很有意义,因为 C 没有任何带有内存管理的框架(如 C# 或 Java)。因此,本例中的 malloc 返回某个地址(非 NULL),它从程序可以访问的程序数据段返回它。

          但是,如果程序很复杂,这样的操作 (*p = 10) 可能会覆盖属于某个其他变量或对象(甚至是指针!)的数据,并导致未定义的行为(包括 seg 错误)。

          但请注意,正如其他答案中所述,此类程序不是最佳实践,您不应在生产中使用此类方法。

          【讨论】:

            【解决方案5】:
            This is a source code of malloc() (maybe have a litter difference between kernel versions but concept still like this), It can answer your question:
            
            static void *malloc(int size)
            {
            void *p;
            
                     if (size < 0)
                    error("Malloc error");
                     if (!malloc_ptr)
                    malloc_ptr = free_mem_ptr;
            
                     malloc_ptr = (malloc_ptr + 3) & ~3;     /* Align */
            
                     p = (void *)malloc_ptr;
                     malloc_ptr += size;
            
                     if (free_mem_end_ptr && malloc_ptr >= free_mem_end_ptr)
                    error("Out of memory");
            
                     malloc_count++;
                     return p;
              }
            

            当你分配 size 为 0 时,它已经给了你一个指向内存首地址的指针

            【讨论】:

              猜你喜欢
              • 2016-08-26
              • 2023-03-07
              • 2021-10-16
              • 2016-09-04
              • 1970-01-01
              • 2012-08-23
              • 2017-09-19
              • 1970-01-01
              相关资源
              最近更新 更多