c-为什么我可以初始化一个分配了零字节的指针



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

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

#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p = malloc(0);
*p = 10;
printf("Pointer address is: %pn", p);
printf("The value of the pointer: %dn", *p);
return 0;
}

这是我的程序输出,我预计会出现分段错误。

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

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

取消引用malloc(0)返回的指针是一种未定义的行为,包括程序可能会错误执行(崩溃或无声地生成不正确的结果),或者它可能会意外地完全按照程序员的意图执行。


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

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

当您调用malloc(0)并写入返回的缓冲区时,您会调用未定义的行为。这意味着您无法预测程序将如何运行。它可能会崩溃,可能会输出奇怪的结果,或者(在这种情况下)它可能看起来工作正常。

程序可能崩溃并不意味着

通常,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)的这一点,"返回的指针不应用于访问对象。">

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

以下是分段故障的一些典型原因:

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

因此,通常情况下,访问程序数据段中的任何地址都不会导致seg故障。这种方法让人觉得C没有任何内存管理框架(比如C#或Java)。所以,当malloc在这个例子中返回一些地址(不是NULL)时,它从程序可以访问的数据段中返回它。

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

但请考虑到,正如其他答案中所述,这种程序不是最佳实践,您不应该在生产中使用这种方法。

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;
}

当您指定的大小为0时,它已经为您提供了一个指向内存第一个地址的指针

最新更新