C中的内存分配-引用释放/释放位置并不总是导致segfault

C中的内存分配-引用释放/释放位置并不总是导致segfault,c,C,根据c编程理论,变量a应该是一个悬空指针,并且应该抛出分段错误。但这里变量a仍然指向同一个内存位置 #include<stdio.h> #include<stdlib.h> int main() { int* a = (int*)malloc(sizeof(int)); printf("%lu\n", sizeof(a)); free(a); *a = 20; printf("%d\n", (*a)); return 0

根据c编程理论,变量a应该是一个悬空指针,并且应该抛出分段错误。但这里变量a仍然指向同一个内存位置

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

int main() {

    int* a = (int*)malloc(sizeof(int));
    printf("%lu\n", sizeof(a));
    free(a);
    *a = 20;
    printf("%d\n", (*a));
    return 0;
}

这是怎么发生的?我在GCC和Clang上执行了这个程序。他们都生产相同的产品

释放分配后,尝试使用该内存会调用未定义的行为。分段错误是UB的许多副作用之一,但不是保证的副作用。

其未定义的行为。您可能会/可能不会出现seg故障。在freea之后,a是一个不指向任何位置的悬空指针。free只释放malloc分配的内存块,它不会改变进程地址空间中指向堆的指针的值。在某些平台上,如果在释放指针后尝试解引用指针,可能会出现segfault。如果在释放指针后将指针指定为NULL,这是一个很好的做法。

正如其他人已经指出的,访问释放的指针是未定义的行为

即使在free调用之后它仍然可用的原因是,在free的大多数实现中,出于性能原因,该函数不会立即向操作系统释放内存位置。但是,释放的内存位置现在是可重用的

以下是malloc和free可能的行为示例:

int*a=mallocsizeofint;//指针'a'的地址为0xABCD 弗里亚;//地址0xABCD现在被视为已释放 *a=5;//这是因为还没有人碰过它 int*b=mallocsizeofint;//malloc将重用地址0xABCD,因为它被认为是免费的 当然,这只是一种可能的行为

TL;DR:自由后不要使用指针,你不知道会发生什么。这是一种极其危险和无用的做法


根据c编程理论,变量a应该是一个悬空指针,并且应该抛出分段错误

C中没有这样的理论或规则

内存分配保留内存供您使用。免费呼叫将删除预订。空闲例程不需要将内存标记为不可访问

C标准定义了一个抽象模型,其中malloc为一个对象保留空间,并自由结束该对象的生命周期。在这个模型中,C标准中的两条规则特别适用于您的问题。C 2018第6.2.4 2节规定:

…如果对象在其生存期之外被引用,则该行为未定义

在C标准中,“未定义”是指本标准未规定任何要求。这意味着标准并没有说你可以访问内存,也没有说你不能访问内存,也没有说你的程序会像对象还在那里一样运行,也没有说你的程序会像对象不在那里一样运行。它并没有说你的程序是否会崩溃。C标准根本没有说明会发生什么

该段还说:

…当指针指向或刚刚经过的对象到达其生命周期结束时,指针的值将变得不确定

这意味着,在freea之后,a的值是不确定的。它不再由C标准规定。C标准中的这条规则之所以出现,是因为一些C实现必须使用各种辅助手段来引用内存,例如包含数据的指针,该数据引用了包含更多信息的表或结构。一旦释放内存,表或结构本身可能会被更改或破坏,然后尝试使用指针将以各种方式失败。因此,在C模型中,指针的值是不确定的。这意味着您的句子“但是这里变量a仍然指向相同的内存位置”没有被C标准指定为true。而且,虽然普通的现代C实现没有依赖于额外数据的指针,但该规则仍然是一条规则,编译器可以在优化过程中利用它。因此,在指针被传递到free之后,您不能依赖它的值


需要明确的是:您不仅不能依赖指针指向的内存,还不能依赖指针的值。简单地执行自由;如果a==NULL…有未定义的行为。

它应该抛出分段错误。错误的访问无效内存是未定义的行为。这意味着它可能有seg故障,但也可能有任何其他行为。在其他一些平台上,您可能最终会遇到这种情况。
8
20