在 C 中获取未定义或实现定义行为的无效指针的值?

Is fetching the value of an invalid pointer undefined or implementation defined behaviour in C?

根据 ,获取无效指针的值是 C++ 中实现定义的行为。现在考虑以下 C 程序:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    int* p=(int*)malloc(sizeof(int));
    *p=3;
    printf("%d\n",*p);
    printf("%p\n",(void*)p);
    free(p);
    printf("%p\n",(void*)p); // Is this undefined or implementation defined in behaviour C? 
}

但是 C 中的行为也相同吗?上述 C 程序的行为是未定义的还是已定义的? C99/C11 标准对此有何评论? 请告诉我 C99 和 C11 中的行为是否不同。

根据 C standard, section 6.2.4:

The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address, and retains its last-stored value throughout its lifetime. If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.

从评论继续。我认为关于它是有效还是无效的混淆围绕着指针的哪个方面被询问。上面,free(p);影响p指向的内存块的起始地址,它不影响p本身的地址,它仍然有效。 p 不再有地址(因为它的值),在重新分配之前它是不确定的。一个简短的例子有帮助:

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

int main (void) {

    int *p = NULL;

    printf ("\n the address of 'p' (&p) : %p\n", &p);

    p = malloc (sizeof *p);
    if (!p) return 1;

    *p = 3;

    printf (" the address of 'p' (&p) : %p   p points to %p   with value  %d\n",
            &p, p, *p);

    free (p);

    /* 'address of p' unchanged, p itself indeterminate until reassigned */
    printf (" the address of 'p' (&p) : %p\n\n", &p);

    p = NULL;  /* p no longer indeterminate and can be allocated again */

    return 0;
}

输出

$ ./bin/pointer_addr

 the address of 'p' (&p) : 0x7fff79e2e8a0
 the address of 'p' (&p) : 0x7fff79e2e8a0   p points to 0x12be010   with value  3
 the address of 'p' (&p) : 0x7fff79e2e8a0

p 本身的地址未被 mallocfree 更改。受影响的是 p 的值(或者更准确地说,地址 p 存储为它的值)。 free 后,地址 p 存储被释放给系统,无法再通过 p 访问。一旦你明确地重新分配 p = NULL; p 就不再不确定并且可以再次用于分配。)

扩展 Andrew Henle 的回答:

来自 C99 标准 6.2.4:

An object has a storage duration that determines its lifetime. There are three storage durations: static, automatic, and allocated. Allocated storage is described in 7.20.3. […] The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.

然后在 7.20.3.2 中:标准继续描述 malloc()calloc()free(),并提到

The free function causes the space pointed to by ptr to be deallocated.

在 3.17.2 中:

indeterminate value

either an unspecified value or a trap representation

在 6.2.6.1.5 中:

Certain object representations need not represent a value of the object type. If the stored value of an object has such a representation and is read by an lvalue expression that does not have character type, the behavior is undefined. […] Such a representation is called a trap representation.

由于指针变得不确定,并且不确定的值可以是陷阱表示,并且您有一个变量是左值,并且读取左值陷阱表示是未定义的,因此 是的,行为可能未定义。

如果编译器正确地确定代码将不可避免地获取一个 指向已传递给 "free" 或 "realloc" 的对象的指针,即使 代码不会使用由此标识的对象,标准将 对编译器在该点之后可以做什么或不可以做什么没有任何要求。

因此,使用如下结构:

char *thing = malloc(1000);
int new_size = getData(thing, ...whatever); // Returns needed size
char *new_thing = realloc(thing, new_size);
if (!new_thing)
  critical_error("Shrinking allocation failed!");
if (new_thing != thing)
  adjust_pointers(thing, new_thing);
thing = new_thing;

可能在大多数实现上允许代码节省重新计算的工作量 在使用 realloc 收缩分配块的事件中的一些指针 不会导致块移动,但不会有任何非法行为 无条件报告缩减分配的实现 失败,因为如果它没有失败,代码将不可避免地尝试进行比较 涉及指向重新分配的块的指针。就此而言,它也会 与实现一样合法(尽管更少 "efficient") 保持检查 realloc 是否返回 null,但允许任意代码 如果没有则执行。

就我个人而言,我认为阻止程序员在可以跳过某些步骤时确定测试没有什么好处。如果指针不改变,则跳过不必要的代码可能会在使用 realloc 缩小内存块的情况下产生显着的效率改进(允许这样的操作移动块,但在大多数实现中通常不会),但它是目前流行的编译器应用他们自己的积极优化将破坏试图使用此类技术的代码。