当然,分配和释放相同的指针是明确定义的......
void *p1 = malloc(42);
assert(p1);
...
free(p1);
...并且当存在这些整数类型时,通过intptr_t/uintptr_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.33
void *p4 = malloc(42);
assert(p4);
uintptr_t u4 = (uintptr_t) p4;
...
void *p5 = (void *) u4;
...
free(p5); // UB or not UB?
因此标题问题似乎已经完成:
“可比”是否足以“匹配”free()
?
我怀疑free(p5)
是undefined behavior(UB),但我不确定。没有特别的应用 - 只是试图理解C的角落,不要急。
答案 0 :(得分:3)
类型uintptr_t保证转换为此类型并返回后的void指针将与原始指针 1 进行比较。
如果指针比较等于另一个指针,则它们指向同一个对象 2 。
因此定义了最后一个示例中的行为,您可以使用该指针释放该对象:free(p5)
1 (引用自:ISO / IEC 9899:201x 7.20.1.4能够保持指针1的整数类型)
以下类型指定无符号整数类型,其属性为any
指向void的指针可以转换为这种类型,然后转换回指向void的指针,
并且结果将与原始指针进行比较:
uintptr_t的
2 (引自:ISO / IEC 9899:201x 6.5.9平等操作员6)
两个指针比较相等,当且仅当两个指针都是空指针时,两个指针都指向
相同的对象(包括指向对象和开头的子对象的指针)或函数,
两者都是指向同一个数组对象的最后一个元素之后的指针,或者一个是指针
到一个数组对象的末尾,另一个是指向另一个数组对象的开头的指针
紧接着地址中第一个数组对象的数组对象
空间。 109)
答案 1 :(得分:3)
参考文献N1570,即2011年ISO C标准(C11)的最新公开草案。
void *p4 = malloc(42);
assert(p4);
uintptr_t u4 = (uintptr_t) p4;
...
void *p5 = (void *) u4;
...
free(p5); // UB or not UB?
uintptr_t
的定义保证指针值p4
和p5
相等;更简洁,p4 == p5
。根据{{1}}对6.5.9p6中指针的定义,我们知道==
和p4
指向同一个对象(因为我们已经确定了{{p5
的值1}}不是空指针。)
这并不能保证它们具有相同的表示形式。标准对指针的表示几乎没有说明(除了它们有一个表示),因此p3
和p4
完全有可能有不同的表示。该标准在6.2.6.1p4中明确允许这一点:
具有相同对象表示的两个值(除NaN之外) 比较相等,但比较相等的值可能有不同的对象 表示。
(实际上可能会或可能不会,具体取决于实施方式。)
现在问题是,究竟是什么传递给p5
?
6.5.2.2中描述了函数调用。第4段说:
在准备调用函数时,会对参数进行求值, 并为每个参数分配相应的值 参数。
(强调补充。)
所以free
看到的不是free()
的对象表示(可能与p5
的对象表示不同),而是p4
的值 {1}},保证与p5
的值相同。传递该值的机制可能涉及制作对象表示的副本,但所有标准都表示该值是传递的,p4
必须处理该值的任何不同表示。同样的价值也是如此。
答案 2 :(得分:2)
有关转换为uintptr_t
的语言使用“比较相等”,但请注意void*
和T*
之间的转换使用相同的词组:
指向
void
的指针可以转换为指向任何不完整或对象类型的指针。指向任何不完整或对象类型的指针可以转换为指向void
并再次返回的指针;结果应该等于原始指针。
(ISO C99标准第6.3.2.3/1节)
如果匹配且比较等于不是同义词,那么根据您的推理,以下也将是UB:
T* p = malloc(n);
...
free(p);
而是需要:
void* p0 = malloc(n);
T* p = p0;
...
free(p0);
这与常识不符。此外,标准和K& R第2版中malloc
使用的示例不做任何此类操作。
答案 3 :(得分:1)
free
不知道/关心返回值的存储位置,或者以后转换为什么类型(它被转换回void*
)。因此,只要它指向已分配且尚未释放的内存,则传入的指针无关紧要。
答案 4 :(得分:0)
通常,只要指向有效的堆块,任何对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并且还没有被释放。