C标准明确表示允许编译器/库组合使用以下代码执行任何操作:
int doubleFree(char *p)
{
int temp = *p;
free(p);
free(p);
return temp;
}
但是,如果编译器不需要使用特定的捆绑库,那么C标准中是否有任何禁止库定义有意义行为的内容?举一个简单的例子,假设代码是为具有引用计数指针的平台编写的,这样在p = malloc(1234); __addref(p); __addref(p);
之后前两次调用free(p)
会减少计数器但不释放内存。编写用于此类库的任何代码自然只能用于这样的库(并且__addref()
调用可能在大多数其他库中失败),但是在许多情况下这样的特征可能有用。有必要将一个字符串重复传递给一个方法,该方法希望给出一个由strdup
生成的字符串,并因此在其上调用free
。
如果库会为某些操作定义一个有用的行为,例如双重释放指针,那么C标准中是否有任何内容可以授权编译器单方面破坏它?
答案 0 :(得分:3)
这里有两个问题,你的正式陈述的问题以及你对其他人提出的问题的评论中概述的更广泛的问题。
您的正式问题是未定义行为的定义和符合性4
部分的答案。定义说(强调我的):
使用不可移植或错误的程序结构或错误数据时的行为, 本国际标准没有要求
强调不可移植和没有要求。这真的说明了一切,编译器可以自由地以令人不愉快的方式进行优化,或者也可以选择使行为记录并明确定义,这当然意味着程序不再严格符合,这就带来了我们到4
部分:
严格符合的程序应仅使用语言和库的那些功能 本国际标准中规定的.2)不得产生任何依赖的输出 未指定,未定义或实现定义的行为,不得超过任何行为 最低实施限制。
但只要不违反合规计划,合规实施就可以延期:
符合要求的实施可能有扩展(包括附加 库函数),只要它们不改变任何严格符合的行为 program.3)
作为C FAQ says:
很少有现实的,有用的,严格遵守的程序。另一方面,仅仅符合程序可以使用它想要的任何特定于编译器的扩展。
你的非正式问题涉及编译器采用未定义行为的更积极的优化机会,并且从长远来看,这将使现实世界系统编程变得不可能。虽然我确实理解这种相对较新的侵略性立场seems very programmer unfriendly to many如果人们不能用它来构建有用的程序,最终编译器会持续很长时间。 John Regehr的一篇相关博客文章:Proposal for a Friendly Dialect of C。
有人可能会反驳说,编译器已经做了很多努力来构建扩展来支持标准不支持的各种需求。我认为文章GCC hacks in the Linux kernel很好地证明了这一点。它涉及Linux内核所依赖的许多gcc扩展,clang
通常尝试支持尽可能多的gcc
扩展。
对于我来说,编译器是否已经删除了妨碍有效系统编程的未定义行为的有用处理。我认为关于未定义行为的个别案例的替代方案的具体问题已经在系统编程中被利用并且不再有效,这对社区来说是有用和有趣的。
答案 1 :(得分:0)
如果库会为某些操作定义一个有用的行为,例如双重释放指针,那么C标准中是否有任何内容可以授权编译器单方面破坏它?
编译器和标准库(即定义free
的库)都是实现的一部分 - 谈论其中一个做某事时,它并不是真正连贯的。单方面"
如果编译器"不需要使用特定的捆绑库",那么(除了可能作为一个独立的实现)它本身不是一个实现,所以标准不适用于它一点都没有。组合库和编译器的行为是谁选择组合它们(可能是任一组件的作者,或完全是其他人)的责任,并将此组合标记为实现。当然,最好不要将库实现的扩展文档作为此实现的功能,而不确认编译器不会破坏它们。就此而言,您还需要确保编译器不会破坏库内部使用的任何内容。
回答你的主要问题:不,它没有。如果组合库和编译器(以及内核,动态加载器等)的最终结果是一致的托管环境,那么即使库的作者希望提供的某些扩展名不是,也是一致的实现。由它们组合的最终结果支持,但它也不要求它们工作。相反,如果结果不符合 - 例如,如果编译器破坏了库的内部,从而导致某些库函数不符合 - 那么它就不是一致的实现。任何在同一指针上调用free
两次的程序,或使用以两个下划线开头的任何保留标识符,都会导致未定义的行为,因此不是严格符合的程序。
答案 2 :(得分:0)
C标准是否要求平台不得定义超出标准
中给出的行为
很简单,不,它没有。标准说:
实施应附有定义所有实施的文件 - 定义的和特定于语言环境的特征和所有扩展。
标准中的任何地方都没有限制禁止实现提供他们喜欢的任何其他文档。如果您愿意,可以阅读最新的ISO C标准草案N1570,并确认没有任何此类禁令。
如果库会为某些操作定义一个有用的行为,例如双重释放指针,那么C标准中是否有任何内容可以授权编译器单方面破坏它?
C实现包括编译器和标准库。 free()
是标准库的一部分。该标准没有定义将同一指针值传递给free()
两次的行为,但实现可以自由定义行为。任何此类文档都不是必需的,超出了C标准的范围。
如果记录了一个C实现,例如,第二次在同一指针值上调用free()
没有效果,但这样做实际上会导致程序崩溃,这会违反实现'自己的文档,但它不会违反C标准。 C标准中没有特别要求,除了标准要求的文档之外,实现必须符合其自己的文档。实现与其自身文档的一致性由市场和常识强制执行,而不是由C标准强制执行。