超出翻译限制是不确定的行为,是否有检查工具来找到它?

原始问题:

我正在搜索C90标准,以便在编写高可移植代码时注意事项,同时对编译器供应商的良好意愿缺乏信任,并假设我的软件有时可能会杀死某人,如果我做错了。 让我们说我有点偏执。

目前我在考虑“翻译限制”(5.2.4.1 ANSI / ISO 9899:1990)。 正如标准中所指出的那样:“ ansi C是否限制了程序中外部变量的数量? ”,这些是标准符合实现的最低要求。 现在另一方面,这意味着,任何实现都不需要做更多 – 如果我想确保我的代码适用于任何confrom实现,这些限制代表我的绝对限制。

到目前为止很烦人。

因此编译器供应商选择的限制等于或高于最低要求的转换限制。

如果超出特定实施的实施定义的转换限制,现在会发生什么? 在我的ANSI / IO 9899:1990(C90)副本中,我没有找到任何东西,所以我认为它是“3种类型的未定义行为”(通过省略)。 另一方面,这不是第一次,我误解了标准或没有找到合适的通道。

所以这是我的问题:

  • IS是否超过C90中特定实现未定义行为的转换限制?

  • C90行为是否适用于C95 / C96以及新迭代C99和C11的修正版本?

  • 有没有人看过那里的检查工具,检查最小的或(工具)用户定义的限制?

超出原始问题的方面:

答案和评论中有趣的方面:

1)正如迈克尔·伯尔在对该问题的直接评论中指出的那样,根据C标准(我只检查了没有更正的C90,以及C99草案,Michael 在这里引用),符合C的实现只需要接受一个程序,它同时包含所有限制,在最严格的解释中取消任何最小限制保证。

2)正如rubenvb和Keith Thompson指出的那样,某些质量的实现应该为案例提供诊断,超出它们的实现定义限制,特别是如果不符合最低要求(rubenvb在评论中链接了MSVC的示例) 。

3)由于超出编译器限制可能是未定义的行为,但肯定会导致某些错误,转换限制适用于我的某段代码的“变量”的值代表重用的前提条件。

我个人的策略来处理它们

1)因此,对于最大的偏执狂,我会愚弄自己,并惹恼编译器供应商的支持,请求保证我,实现选择的限制适用于任何程序。 🙁

2)因此,我将研究编译器文档以及编译器支持获得确认的痛苦程度: – 对于每个转换限制,如果超出,将引发诊断,并且 – 因为它是未定义的行为,如果每个超出翻译限制的实例都会引发诊断 – 或者另一个错误已经阻止了编译。

3)因此,我将尝试开发一个工具(或者如果我真的必须开发自己),测量这些值,并将它们作为我的程序的代码重用的前提条件。 正如Keith Thompson在本回答中指出的那样,一些价值观可能需要更深入地了解实施如何实施。 我不能完全确定在这种情况下除了2)中的行为之外还能提供什么帮助。但是,据我所知,我必须进行测试 – 但我只需要测试是否有UB(没有诊断),如果这是在这种情况下,成功的测试不能保证一般情况下的正确性。

回答说:

是的,它是未定义的行为。

Keith Thompson在他的(接受的) anwser中展示了C标准文档的术语和参考,它是未定义的行为。

评论者尚未(尚未)发现检查代码中的事务限制的工具。 如果任何具有(甚至部分)此function的人都会使用该工具,请留下答案或评论。

我认为这种行为是不确定的。

该标准要求对任何违反约束或语法规则( N1570 5.1.1.3)的翻译单元进行诊断,并且可能无法成功翻译包含在预处理阶段( n1570 4,第4段)中存在的#error指令的翻译单元。 (N1570是C11标准的草案,但在C90,C99和C11之间是相同的,只是C99添加了#error 。)

所有约束和语法规则都在标准中明确指定。 超出实现定义的限制既不违反约束也不违反语法规则。 我认为,足够明显的是,成功处理超出翻译限制的其他正确程序并不需要实现,但标准没有说明它应如何响应此类违规。 因此,遗漏不确定行为。

(体面质量的实施会发出诊断说超过限制,但标准不要求这样做。)

为了回答你问题的第三部分,不,我没有听说过一个静态检查工具来检查程序是否违反了最低翻译限制。 这样的工具可能非常有用,一旦你有一个C解析器,可能写起来也不会太难。 对于对象大小的限制(C90中的32767个字节,C99和C11中的65535个字节),它必须知道编译器如何确定对象大小; int arr[30000]; 可能会或可能不会超过65535字节,具体取决于sizeof (int) 。 如果有人已经实施了这样的工具并且我没有听说过它,我不会感到惊讶。

请注意,大多数实现都没有强加标准允许的固定限制; 相反,任何限制都是由编译时可用的内存资源强加的。

该标准确实以相当奇怪的方式呈现了翻译限制。 我特别想到的条款是:

该实现应能够翻译和执行至少一个包含以下每个限制的至少一个实例的程序:

(这是C90,C99和C11中的5.2.4.1节)。 因此,不正当的实现可以接受一个程序并拒绝所有其他程序。

我认为,关键在于指定所有实现必须满足的合理限制是不切实际的。 标准可以说所有实现必须始终接受至少32767字节的对象 – 但是定义了一百万个这样的对象的程序呢? 限制以极其复杂的方式相互影响,交互的性质取决于每个编译器的内部结构。 (如果您认为可以比C标准更好地定义翻译限制的要求,我建议您尝试一下。)

相反,标准以这样的方式陈述要求:实现遵循标准字母的有用编译器的最简单方法是通过不施加任何不合理的限制来实现遵循标准精神的有用编译器。 符合标准字母的无用编译器是可能的,但不相关; 我不知道有人曾经实施过这样的事情,我相信没有人会尝试使用它。

它不是未定义的行为,它是实现定义的行为。 这意味着一切都取决于编译器。

是的,最低实施指南保持不变或扩展到新标准版本。

您可能可以使用Clang,但是您需要使用Clang API自己编写工具,我不知道预先存在的实现。


在任何情况下:限制不是由标准设定的,“他们更像[指南]反正”,(实际上只不过是指导方针)。 您需要检查用于构建代码的编译器,看看您是否达到了任何限制,只能在某人的鼻子上挥舞标准文档。 而且由于MSVC的实现特别糟糕,我甚至敢说,如果它编译你的代码(假设代码本身没有非法的结构),你就很安全了。

在某些环境中,应用程序可能会收到等于可用总内存的堆栈空间,减去代码和静态数据的组合大小。 如果在努力运行程序之前无法知道可用内存的数量,则编译器,链接器或任何其他此类工具可能无法知道它是否足够。 如果在没有足够的内存可用于处理其堆栈要求时尝试运行程序,则标准中的任何内容都不会对必须发生的情况施加任何要求。

如果标准提供了一种方法,通过该方法,程序可以确保在使用任何可用内存量运行时某些可预测行为的度量,但目前它不会这样做。 在许多平台上,将会有一些可用的内存,这些内存足够大,以至于OS加载程序不会拒绝可执行文件,但是仍然足够小,以至于应用程序在启动时几乎立即遭受堆栈溢出。 C标准的作者不想声明C不能用于这样的平台,但他们也无法真正说明在尝试运行具有大量内存的代码时平台将会做什么。