5

根据this ,获取无效指针的值是 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 中的行为是否不同。

4

4 回答 4

7

扩展 Andrew Henle 的回答:

来自 C99 标准,6.2.4:

对象具有决定其生命周期的存储持续时间。共有三种存储持续时间:静态、自动和分配。分配的存储在7.20.3 中描述。[…]当指针指向(或刚刚过去)的对象到达其生命周期的尽头时,指针的值变得不确定。

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

free函数使指向的空间ptr被释放。

在 3.17.2 中:

不确定值

未指定的值或陷阱表示

在 6.2.6.1.5 中:

某些对象表示不需要表示对象类型的值。如果对象的存储值具有这样的表示形式并且由不具有字符类型的左值表达式读取,则行为未定义。[…] 这种表示称为陷阱表示。

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

于 2015-11-07T17:06:22.860 回答
4

根据C 标准,第 6.2.4 节

对象的生命周期是程序执行期间保证为其保留存储的部分。一个对象存在,有一个不变的地址,并在其整个生命周期中保留其最后存储的值。如果对象在其生命周期之外被引用,则行为未定义。 当指针指向(或刚刚过去)的对象到达其生命周期的末尾时,指针的值变得不确定

于 2015-11-07T16:21:09.180 回答
0

如果编译器正确地确定代码将不可避免地获取指向已传递给“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 收缩分配的块不会导致块移动,但是对于无条件报告收缩分配的实现不会有任何非法失败,因为如果它没有失败,代码将不可避免地尝试涉及指向重新分配块的指针的比较。就此而言,实现保持检查 realloc 是否返回 null 也是合法的(尽管“效率较低”),但如果没有,则允许执行任意代码。

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

于 2015-11-10T00:08:17.437 回答
-2

继续评论。我认为关于它是有效还是无效的混淆围绕着指针的哪个方面被询问。上面,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不会被malloc或改变free。影响的是p(或更准确地说,地址p存储为其值)的值。free之后,地址存储被释放p到系统,不能再通过p。一旦你明确地重新分配p = NULL; p不再是不确定的,可以再次用于分配。)

于 2015-11-07T16:44:36.770 回答