我希望这不是重复,如果是我道歉。
编辑:这是重复的,因为之前已经提出了类似的问题。这个问题的答案并不合适。为了更好地了解我想要达到的微妙之处,请阅读我关于未初始化内存的评论。编辑结束。
我的问题是下面的C代码在某种意义上是否合法。即使它是,我也不会使用它,因为它会产生警告和警告让我恼火。不过,我很好奇。但足够的漫无边际的介绍,问题本身就是漫无目的。设置如下。
假设您要对整数数组进行排序。你没有实现自己的排序,而是决定使用qsort,它需要一个带有两个void *的比较函数。在函数内部,将void *转换为int *,然后比较它们。
在理想的世界中,我想传递一个需要两个int *的函数。如果您尝试这样做,编译器会发出警告,原因很明显:尽管如此,qsort会尝试使用short *和double *来调用您的函数,然后会发生混乱。如果C类型系统更具表现力,那么qsort的声明可以给编译器足够的信息来推断这种情况永远不会发生。
但即使使用我们的类型系统,一个Sufficiently Smart Compiler(tm)确切地知道qsort是如何工作的,因此它知道你的比较函数总是会得到指向整数的指针。不幸的是,即使这样,编译器也必须发出警告,因为如果编译器真的是可移植的,它可能必须支持void *的大小与int *的大小不同的平台。
但是这种平台非常罕见,接近于不存在。更常见的是int只有2个字节的平台,在这些平台上,表达式123 * 456是未定义的行为。但是今天的大多数平台都有一个更大的int,而那些表达式已经明确定义了。如果具有4字节整数的编译器假定123 * 456无法访问,则该编译器不符合标准。换句话说,一旦实现产生了一些承诺(大型int),标准就禁止它做一般允许的事情(将某些表达式视为溢出)。
让我们再看一个例子。在大多数操作系统中,取消引用空指针会使程序崩溃。但即使你不介意崩溃,你仍然必须检查malloc的返回值,因为取消引用null是根据标准的未定义行为,并且很少有编译器在这种情况下做出更强的保证。
我的问题是:函数指针在哪里落在这个连续体上?我很确定今天常用的大多数平台确实保证所有指针都具有相同的表示,使用相同的调用约定传递等等。如果我只关心那些平台,我可以传递一个比较函数采用int *而不是void *,就像我可以无畏地乘以3位数字一样,或者更像是解除引用null的情况,除非文档明确说明,否则这种特定行为是未定义的?