【问题标题】:Does *(NULL) compile successfully by the C99 standard?*(NULL) 是否按 C99 标准成功编译?
【发布时间】:2013-10-17 16:44:54
【问题描述】:

我知道这段代码永远不应该被执行,但*(NULL) 是否按 C99 标准成功编译?

#include <stdio.h>
int main(void) {
    *(NULL);
    return 0;
}

PS: 在ideone 上运行它返回 0 但我不知道正在使用的编译器版本:(

【问题讨论】:

  • “在 ideone 上运行它似乎有效”:定义“工作”。你的意思是它按预期崩溃还是返回错误代码0
  • 是的。 C99 将允许你编译它。
  • C99 不“允许”它。
  • 请澄清问题。你是在问代码是否应该编译,或者它的行为是否定义良好?

标签: c c99


【解决方案1】:

如果NULL 被简单地定义为0(这是允许的),那么*(0) 是违反约束的,必须进行诊断。如果 NULL 定义为 ((void *)0)*((void *)0) 调用未定义的行为(通过取消引用空指针)并且编译器不需要发出任何诊断,但程序也不需要按照您期望的方式运行.

【讨论】:

  • 我的印象是,无论指针值是否有效,在 C 中取消引用 void * 指针总是非法的。这意味着即使对于NULL(void *) 0 定义,代码也包含约束违规。该标准并没有明确地拼写它......
  • 所以是违反约束还是未定义行为的问题是实现定义的。
  • 您可能想提一下,取消引用 void* 指针本身并不是违​​反约束(尽管我不知道为什么不是)。
  • 我能想到的唯一可能合法取消引用void* 的情况是&amp;(*((void*)ptr))(可能是一系列宏扩展的产物)。
  • @pburka:这不算作取消引用。请参阅 C99 6.5.3.2 地址和间接运算符,第 3 段:“如果操作数是一元 * 运算符的结果,则该运算符和 & 运算符都不会被计算,结果就好像两者都被省略了,除了对运算符仍然适用,结果不是左值。”
【解决方案2】:

*(NULL) 总是违反约束。在NULL 的任何定义下,*(NULL) 作为完整表达式在 C 中“不可编译”。 NULL 可以定义一个整数零,这显然是不可解引用的。 NULL 可以定义为整数零转换为 void * 类型,但在 C 中取消引用 void * 指针是非法的。

注意:我仍然不能完全确定我上面关于取消引用 void * 指针的说法是否正确。是否违反约束?该标准没有明确说明。但同时它为函数指针和对象指针定义了一元 * 的行为。同时,void 不是对象类型。

如果取消引用void * 指针确实是合法的,那么*(NULL) 本身会产生未定义的行为(假设(void *) 0 定义了NULL)。但是仍然有一个上下文*(NULL) 可能是有效的作为子表达式。它是&amp;*(NULL)。该语言对&amp;* 组合进行了特殊处理,使其成为具有明确行为的无操作,即使在单独* 会产生未定义行为的情况下也是如此。

【讨论】:

  • +1 only 正确答案(到目前为止)。我唯一的疑问是 C 标准没有使用“非法”一词。即使存在约束违规或语法错误,编译器也可能会发出警告(有资格作为“诊断”)并继续编译。
  • 抱歉,我说得太早了。将void* 视为违反约束当然是有意义的,我认为是这样。但N1570 6.5.3.2 中唯一相关的约束是“一元 * 运算符的操作数应具有指针类型。”这让我想知道int n; void *ptr = &amp;n; &amp;ptr;&amp;ptr 将是 void 类型的表达式,这是允许的。当在编译时很容易检测到时,标准将行为未定义是很奇怪的。
  • @Keith Thompson:我就是这么想的。 (顺便说一句,我想你的意思是 *ptr 在你的评论中)。注意*ptr 不仅仅是void 类型的表达式,它实际上是void 类型的左值。我看到这种左值的唯一用途是立即成为一元 &amp; 的操作数。也许这就是它被允许的原因。
  • 严格来说,如果*void_pointer 是一个,那么任何对void 函数的调用都将违反约束。除了任何副作用之外,我看不出void_returning_function();*void_pointer; 之间有什么区别。因此,唯一的不确定性可能源于取消引用 NULL,这确实可能存在问题。
  • @glglgl:首先,void_returning_function()*void_pointer 的区别是相当大的:前者不是左值,后者是。 IE。它是值上下文和对象上下文之间的区别。 *void_pointer 指的是什么对象?其次,虽然允许*void_pointer 只是因为我们可以有 void 函数似乎是合乎逻辑的,但语言并不总是遵循这种逻辑。例如,在 C 中,执行 void foo() { return foo(); } 是非法的,即从 void 函数返回 void "values" 是非法的。 (它在 C++ 中是合法的,顺便说一句)。
【解决方案3】:

*(NULL) 是未定义的行为。

来自马口:

(C99, 6.5.3.2.p4) "如果给指针赋值了一个无效值,一元 * 操作符的行为是未定义的。87)"

并且(强调我的):

87): "一元 * 运算符取消引用指针的无效值包括空指针、与指向的对象类型不恰当对齐的地址以及对象的地址在其生命周期结束之后。”

当然,NULL 是一个空指针:

(C99, 7.17p3) "宏 [..] NULL 扩展为实现定义的空指针常量;"

与所有调用未定义行为的程序一样,编译器有权不编译它(参见 C99,3.4.3p2)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-12
    • 2018-02-11
    • 1970-01-01
    • 2011-01-08
    • 2013-08-27
    • 2012-11-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多