int main()
{
int x = 0;
free(x);
}
Run Code Online (Sandbox Code Playgroud)
这编译并且似乎是无操作.究竟发生了什么?是这个行为定义的?
谢谢!
AnT*_*AnT 14
不,行为未定义.而且,代码不应该编译.
首先,代码不应该编译,因为它包含约束违规.您作为操作数传递的表达式free具有int类型.free有void *类型的参数.当值是具有值的积分常量表达式(ICE)时,唯一int可以将值隐式转换为void *类型的情况.在你的情况下,不是ICE,意味着它不能隐式转换为.您的代码编译的唯一原因是,由于历史原因(支持遗留代码),您的编译器会悄悄地忽略调用中存在的约束违规.我敢肯定,如果你提升编译器中的警告级别,它会抱怨(至少有警告).一个迂腐的编译器会立即发出一个错误的呼叫.尝试使用Comeau Online,例如在C89/90模式下:int0xvoid *free(x)free(x)
"ComeauTest.c", line 6: error: argument of type "int" is incompatible with parameter
of type "void *"
free(x);
^
Run Code Online (Sandbox Code Playgroud)
(另外,你还记得stdlib.h在打电话之前加入free吗?)
其次,让我们假设代码编译,即编译器将其解释为free((void *) x).在这种情况下,非常量整数值x被转换为指针类型void *.此转换的结果是实现定义.注意,该语言保证当具有值的ICE 0转换为指针类型时,结果是空指针.但在你的情况下x不是ICE,所以转换的结果是实现定义的.在C中,无法保证通过将带有值的非ICE整数转换0为指针类型来获取空指针.在你的实现上,可能只是因为(void *) x非ICE x等于0产生类型的空指针值void *.传递给此时free,此空指针值会根据规范生成no-op free.
但是在一般情况下,传递这样的指针free将导致未定义的行为.到你可以合法地通过指针free的指针以前的调用来获得malloc/ calloc/ realloc和空指针.在一般情况下,您的指针违反此约束,因此行为未定义.
这就是你的情况.但是,您的代码再次包含约束违规.即使您覆盖违规,行为也是未定义的.
PS注意,顺便说一句,这里已经发布的许多答案都会犯同样严重的错误.他们假设(void *) x零x应该产生一个空指针.这绝对不正确.同样,该语言绝对不能保证(void *) x何时x不是ICE 的结果.(void *) 0被保证是空指针,但(void *) x具有零x是不保证是空指针.
这在C FAQ http://c-faq.com/null/runtime0.html中有所介绍.对于那些有兴趣更好地理解其原因的人来说,阅读关于空指针的整个部分可能是一个好主意http://c-faq.com/null/index.html
Eri*_* Pi 12
在你的情况下,它的工作原理是因为调用free(NULL)(即free(0))是一个NOOP.但是,如果使用0以外的任何值(NULL)调用它,则行为将是未定义的 - 崩溃和/或内存损坏可能是候选者.
编辑:正如其他人后来指出的那样,free(x)(x = 0)和free(NULL)不是一回事.(虽然它通常为0,但NULL指针的值是实现定义的,不能依赖它.)请参阅AndreyT的答案以获得非常好的说明.