C标准是否要求平台不得定义超出标准规定的行为

C标准明确表示允许编译器/库组合使用以下代码执行任何操作:

int doubleFree(char *p) { int temp = *p; free(p); free(p); return temp; } 

但是,如果编译器不需要使用特定的捆绑库,那么C标准中是否有任何禁止库定义有意义行为的内容? 举一个简单的例子,假设代码是为具有引用计数指针的平台编写的,例如在p = malloc(1234); __addref(p); __addref(p); p = malloc(1234); __addref(p); __addref(p); 前两次调用free(p)会减少计数器但不释放内存。 编写用于此类库的任何代码自然只能用于这样的库(并且__addref()调用可能会在大多数其他库中失败),但是在许多情况下这样的特性可能会有所帮助,例如,有必要通过一个字符串重复到一个方法,该方法希望给出一个用strdup生成的字符串,因此在其上调用free

如果库会为某些操作定义一个有用的行为,例如双重释放指针,那么C标准中是否有任何内容可以授权编译器单方面破坏它?

这里有两个问题,你的正式陈述和你在其他人提出的问题的评论中概述的更广泛的问题。

您的正式问题是未定义行为的定义和关于一致性的第4部分的答案。 定义说( 强调我的 ):

行为,在使用不可移植或错误的程序结构或错误数据时,本国际标准对此要求

重点是不便携并且没有要求 。 这真的说明了一切,编译器可以自由地以令人不愉快的方式进行优化,或者也可以选择使行为记录并明确定义,这当然意味着程序不再严格符合 ,这将我们带到第4节:

严格符合的程序应仅使用本国际标准中规定的语言和库的那些特征.2)它不应产生依赖于任何未指定,未定义或实现定义的行为的输出,并且不得超过任何最小实现限制。

但只要不违反合规程序,符合条件的实现才允许扩展:

符合条件的实现可能具有扩展(包括其他库函数),前提是它们不会改变任何严格符合程序的行为.3)

正如C FAQ所说 :

很少有现实的,有用的,严格符合的程序。 另一方面,仅仅符合程序可以使用它想要的任何特定于编译器的扩展。

您的非正式问题涉及编译器采取具有未定义行为的更积极的优化机会,并且从长远来看,这将使现实世界系统编程变得不可能。 虽然我确实理解这种相对较新的攻击态度对于许多程序员来说似乎对程序员不友好,但如果人们无法用它来构建有用的程序,那么编译器将不会持续很长时间。 John Regehr撰写的相关博客文章: C语言友好方言的提案 。

有人可能会反驳说,编译器已经做了很多努力来构建扩展来支持标准不支持的各种需求。 我认为Linux内核中的GCC文章很好地certificate了这一点。 它涉及Linux内核所依赖的许多gcc扩展,并且clang一般都试图支持尽可能多的gcc扩展。

编译器是否已经删除了对妨碍有效系统编程的未定义行为的有用处理,我不清楚。 我认为关于未定义行为的个别案例的替代方案的具体问题已经在系统编程中被利用并且不再有效,这对社区来说将是有用和有趣的。

C标准是否要求平台不得定义超出标准规定的行为

很简单,不,它没有。 标准说:

实现应附带一个文档,该文档定义所有实现定义的和特定于语言环境的特征以及所有扩展。

标准中的任何地方都没有限制,禁止实现提供他们喜欢的任何其他文档。 如果您愿意,可以阅读N1570 ,这是ISO C标准的最新免费草案,并确认没有任何此类禁令。

如果库会为某些操作定义一个有用的行为,例如双重释放指针,那么C标准中是否有任何内容可以授权编译器单方面破坏它?

AC实现包括编译器和标准库。 free()是标准库的一部分。 该标准没有定义将相同的指针值传递给free()两次的行为,但是实现可以自由地定义行为。 不需要任何此类文档,这超出了C标准的范围。

如果记录了一个C实现,例如,在同一指针值上第二次调用free()没有效果,但这样做实际上会导致程序崩溃,这会违反实现自己的文档,但不会违反C标准。 C标准中没有特别要求说实现必须符合自己的文档,超出了标准要求的文档。 实现与其自身文档的一致性由市场和常识强制执行,而不是由C标准强制执行。

如果库会为某些操作定义一个有用的行为,例如双重释放指针,那么C标准中是否有任何内容可以授权编译器单方面破坏它?

编译器和标准库(即定义了free的那个)都是实现的一部分 – 谈论其中一个“单方面”做某事并不是真的一致。

如果编译器“不需要使用特定的捆绑库”,那么(除了可能作为独立实现之外)它本身不是一个实现,因此该标准根本不适用于它。 组合库和编译器的行为是谁选择组合它们(可能是任一组件的作者,或完全是其他人)的责任,并将此组合标记为实现。 当然,最好不要将库实现的扩展文档作为此实现的function,而不确认编译器不会破坏它们。 就此而言,您还需要确保编译器不会破坏库内部使用的任何内容。


回答你的主要问题:不,它没有。 如果组合库和编译器(以及内核,动态加载器等)的最终结果是一致的托管环境,即使最终不支持库的作者想要提供的某些扩展,它也是一致的实现。将它们组合起来的结果,但也不要求它们起作用。 相反,如果结果不符合 – 例如,如果编译器破坏了库的内部,从而导致某些库函数不符合 – 那么它就不是一致的实现。 任何在同一指针上free调用两次的程序,或使用以两个下划线开头的任何保留标识符,都会导致未定义的行为,因此不是严格符合的程序。