是否在C中获取无效指针的值未定义或实现定义的行为?
根据此,获取无效指针的值是C ++中的实现定义行为。 现在考虑以下C程序:
#include #include 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的行为是否不同。
扩展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中:
某些对象表示不需要表示对象类型的值。 如果对象的存储值具有这样的表示,并且由不具有字符类型的左值表达式读取,则行为是未定义的。 […]这种表示称为陷阱表示。
由于指针变得不确定,并且不确定值可以是陷阱表示,并且您有一个左值变量,并且读取左值陷阱表示是未定义的,因此是,行为可能是未定义的。
根据C标准,第6.2.4节 :
对象的生命周期是程序执行的一部分,在此期间保证为其保留存储。 存在一个对象,具有一个常量地址,并在其整个生命周期内保留其最后存储的值。 如果在其生命周期之外引用对象,则行为未定义。 当指针指向(或刚刚过去)的对象到达其生命周期的末尾时,指针的值变得不确定 。
如果编译器正确地确定代码将不可避免地获取指向已经传递给“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缩小内存块的情况下显着提高效率(允许这样的动作移动块,但在大多数实现中它通常不会),但它是目前,编译器应用他们自己的积极优化时尚,这将破坏试图使用这些技术的代码。
继续评论。 我认为关于它是有效还是无效的混淆包围了指针的哪个方面被问到。 以上, free(p);
将起始地址影响到p
指向的内存块,它不会影响p
本身的地址,它仍然有效。 p
(因为它的值)不再保留地址,直到重新分配为止。 一个简短的例子有助于
#include #include 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) : %pp 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
无论是malloc
还是free
, p
本身的地址都不变。 受影响的是p
的值(或者更确切地说,地址p
存储为其值)。 在free
,地址p
存储被释放到系统,并且不能再通过p
访问。 一旦明确重新分配p = NULL;
p
不再是不确定的,可以再次用于分配。)