C “free(一个指针)”是定义良好的还是UB?
当然,分配和释放同一个指针是定义良好的C “free(一个指针)”是定义良好的还是UB?,c,language-lawyer,C,Language Lawyer,当然,分配和释放同一个指针是定义良好的 void *p1 = malloc(42); assert(p1); ... free(p1); 。。。当这些整数类型存在时,尽管不一定是相同的位模式,但通过intptr\u t/uintptpr\u t进行转换会创建一个可比较的指针(如“比较相等”C11 7.20.1.4)。可以说,p2和p3具有相同的值,但可能具有不同的表示形式 void *p2 = malloc(42); assert(p2); uintptr_t u2 = (uintptr_t)
void *p1 = malloc(42);
assert(p1);
...
free(p1);
。。。当这些整数类型存在时,尽管不一定是相同的位模式,但通过intptr\u t/uintptpr\u t
进行转换会创建一个可比较的指针(如“比较相等”C11 7.20.1.4)。可以说,p2
和p3
具有相同的值,但可能具有不同的表示形式
void *p2 = malloc(42);
assert(p2);
uintptr_t u2 = (uintptr_t) p2;
...
void *p3 = (void *) u2;
// Specified to evaluate true C11 7.20.1.4
if (p2 == p3) ...
// Maybe true, maybe false
if (memcmp(&p2, &p3, sizeof p2) == 0) ...
// Note - early version of post had this errant code
// if (memset(&p2, &p3, sizeof p2) == 0) ...
现在通过可比较的指针转到free()
free
函数导致ptr
指向的空间被释放。。。如果参数与内存管理函数先前返回的指针不匹配。。。行为是未定义的。C11dr§7.22.3.3
因此,标题问题似乎要解决:关于
免费()
“可比”是否足以“匹配”
我怀疑
free(p5)
是(UB),但不确定。没有特别的应用程序-只是尝试理解C的各个角落,没有匆忙。类型uintptru\t保证在转换为该类型和返回后,空指针将与原始指针1相等
如果一个指针与另一个指针比较相等,则它们指向同一对象2
最后一个示例中的行为就是这样定义的,您可以使用该指针来释放对象:free(p5)
1(引用自:ISO/IEC 9899:201x 7.20.1.4能够容纳指针的整数类型1)
以下类型使用任何有效的属性指定无符号整数类型 指向void的指针可以转换为此类型,然后再转换回指向void的指针, 结果将与原始指针进行比较: uintptr\t 2(引用自:ISO/IEC 9899:201x 6.5.9等式运算符6)
两个指针比较相等当且仅当两个指针都是空指针时,两个指针都是指向 相同的对象(包括指向对象的指针和其开头的子对象)或函数, 两者都是指向同一数组对象的最后一个元素的指针,或者一个是指针 一个指向一个数组对象的末尾,另一个指向另一个数组对象的开始的指针 恰好紧跟地址中第一个数组对象之后的数组对象
空间109)
free
不知道/不关心返回值存储在哪里,也不关心它后来转换成什么类型(它被转换回void*
)。因此,您传入的指针并不重要,只要它指向已分配但尚未释放的内存即可 一般来说,对free的任何调用只要指向有效的堆块就可以了。当参数不完全指向有效堆块的开头时,空闲行为变得不可预测。例如,下面的代码将编译,但在执行过程中会崩溃,因为修改后的指针未指向有效的堆块:
int* pn = (int*)malloc(sizeof(int));
pn += 2; // Now points beyond the beginning of an allocated heap block
void* pv = (void*)pn;
free(pv); // Therefore will crash
如果您删除pn+=2,它将再次正常。
底线:修改和强制转换指针是可以的,但是当你释放它们时,你应该确保它们指向实际分配但尚未解除分配的saomething。有关转换到和从
uintpttr\t
转换的语言使用“比较相等”,但请注意,void*
和T*
之间的转换使用相同的短语:
指向void
的指针可以转换为指向任何不完整或对象类型的指针,也可以转换为指向任何不完整或对象类型的指针。指向任何不完整或对象类型的指针可转换为指向void
的指针,然后再返回;结果应与原始指针进行比较
(ISO C99标准第6.3.2.3/1节)
如果match和compare equal不是同义词,那么根据您的推理,以下内容也可以是UB:
T* p = malloc(n);
...
free(p);
而是需要:
void* p0 = malloc(n);
T* p = p0;
...
free(p0);
这违背了常识。此外,本标准和K&R第二版中的malloc
用法示例并未进行类似操作。参考2011年ISO C标准(C11)的最新公开草案
uintpttr\t
的定义保证指针值p4
和p5
比较相等;更简洁地说,p4==p5
。根据6.5.9p6中指针的==
定义,我们知道p4
和p5
指向同一个对象(因为我们已经确定p3
的值不是空指针)
这并不保证它们具有相同的表示形式。该标准很少涉及指针的表示(除了它们有一个表示),因此p4
和p5
完全可能有不同的表示。本标准在6.2.6.1p4中明确允许:
具有相同对象表示形式的两个值(非NaN)
比较相等,但比较相等的值可能具有不同的对象
陈述
void *p2 = malloc(42);
assert(p2);
uintptr_t u2 = (uintptr_t) p2;
...
void *p3 = (void *) u2;
// Specified to evaluate true C11 7.20.1.4
if (p2 == p3) ...
// Maybe true, maybe false
if (memcmp(&p2, &p3, sizeof p2) == 0) ...
// Note - early version of post had this errant code
// if (memset(&p2, &p3, sizeof p2) == 0) ...
(这可能是也可能不是真的,取决于实施情况。)
现在的问题是,到底传递给免费的是什么
函数调用如6.5.2.2所述。第4段说:
在准备调用函数时,会计算参数,
并为每个参数指定相应参数的值
争论
(重点加上。)
因此free()
看到的不是p5
的对象表示(这可能不同于p4
的对象表示),而是p5
的值,它保证与p4
的值相同。传递该值的机制可能涉及复制对象表示,但标准所说的是传递该值,并且free
必须处理相同值的任何不同表示
void *p4 = malloc(42);
assert(p4);
uintptr_t u4 = (uintptr_t) p4;
...
void *p5 = (void *) u4;
...
free(p5); // UB or not UB?