Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2314876
  • 博文数量: 527
  • 博客积分: 10343
  • 博客等级: 上将
  • 技术积分: 5565
  • 用 户 组: 普通用户
  • 注册时间: 2005-07-26 23:05
文章分类

全部博文(527)

文章存档

2014年(4)

2012年(13)

2011年(19)

2010年(91)

2009年(136)

2008年(142)

2007年(80)

2006年(29)

2005年(13)

我的朋友

分类: C/C++

2011-02-07 21:08:17

读ANSI C标准, 或K&R, 或C: A reference manual时, 往往会碰到对某个语言特性这样的描述. 这三者到底是什么意思, 我的粗略印象, 从implement-defined, 到unspecified, 到undefined, 依次越来越不靠谱, 越发危险, 越发不可移植. 仅有这样的模糊认识是不够的, 实际上, 求证于典籍之后, 还是不十分清朗.

关于为什么会有这3个晦涩的短语, 来迷惑我这样的平民百姓, 难道是所谓语言律师们给我下的套?

Rationale中说:

我的理解是:

未指定的行为, 未定义行为, 和实现定义的行为用于归类那些
本标准没有, 或是不能完全描述其属性的程序的结果. 对其分类的目的是为了允许
不同的编译器实现可以有一定程度的灵活性. 这样也鼓励编译器实现质量成为市场
竞争的主导因素. 同时也能在允许编译器在实现一些受欢迎的语言扩展的同时, 还
能打着遵循语言标准的金字招牌. 标准中告知性的附录J把语言中的这些行为分门
别类地归纳到上述三种行为.


未指定的行为给具体实现者留下了一定的自由度. 这种自由不能越过编译程序失败
的极限(包括编译程序失败, 下面对"未定义行为"的讨论更证实了这一点), 因为无
论如何, 只要任何实现不引起未定义行为, 所有可能的行为都算是"正确的".

未定义行为等于给实现者发了免死金牌, 实现者可以放任程序中存在错误, 只因为
这些错误太难应对. 同时未定义行为也为可能的语言扩展打开了一扇门: 实现者可
以通过对标准中官方声称的未定义的行为作出定义来增强语言.

C99标准中说:
3.4.1  实现定义的
这个是三者之中最好理解的, 它有几个特点:
1. 具体的编译器自己做决定. 
2. 但不能是拒绝编译, 也就是说不能编译失败.
3. 编译器必需在文档中说明它选择的行为.
4. 标准本身并不提供可选的方案(与 unspecified 相比)

3.4.3 未定义的行为

基本上, 未定义最能让具体编译器天马行空, 为所欲为. 包括编译失败. 这可能是最省事的做法.
举例说不可移植的特性, 或错误的程序结构(gcc的__attribute__((***)), 或是在表达式中允许块结构, 算不算是), 或是错误的数据(这个想不出例子). 对出现这些情况, 而本标准又对具体实现未作要求的. 算作未定义行为.

举例是整型溢出.

3.4.4 未指定的行为

标准中给定了2种或更多的选择, 除此之外标准没有额外地要求什么情况该选择哪种行为.
也就是说, 未指定的行为是让编译器实现者做选择题. 但没有要求其选择必需是一致的, 编译器可以在同一个编译单元中, 一会儿选择这种做法, 一会选择另外的做法.

久负盛名的C FAQ(中译名称是<<你必须知道的495个C语言问题>>)中关于这个问题, 也有相关的讨论


这里把unspecified的翻译为"不确定的", 我觉得有些不妥, 这里的解释说, unspecified, 跟未定义类似, 但无需提供文档. 这加剧了我的一些迷惑, 原因是这个C FAQ应该也是相当的权威. "类似"是个模糊的词, 不中心解惑这个问题. 另外, 至少unspecified 在一些明确的方面和未定义不同, 那就是, 实现者的行为上限是编译失败(包括编译失败), 虽然我想不出一个编译器所能引起的比编译失败更严重的后果, 但正如它所说, 未定义行为比你想象的还要未定义, 或许它可以让电脑死机, 引发太阳黑子.


该书中的最后陈述乍一看很有建设性. 那就是, 不管这三种行为谁是谁, 你全都要避免踩到雷区. 要求也不小, 你要能记住语言中所有的 implement-defined, unspecified, undefined的行为. 不容易.

但是, 看了The New C standard之后, 就会知道, 要避免所有的unspecified行为几乎不可能, 比如b+c 表达中, b和c谁先被读取就是未指定的. 该书中说:
A blanket guideline recommendation prohibiting the use of any construct whose behavior is unspecified would be counterproductive.

光是 unspecified 的行为, 我统计了一下, 50条(统计完这个我发现<>中就有这么一句话, 所以我只统计了这一个, undefined的个数是直接从该书引用了, 190个. )

从这50条分析来看, 并非所有的未指定行为, 都是在语言标准中给了可选方案, 让实现者做选择就可以了, 比如对静态变量的初始化的方式和时机. 标准中就没有给出任何建议的方案.

下面我把C99 标准中的这几页PDF 抽出来, 单存成一个文件. 有兴趣的可以自己看看.
文件:C99_unspecified_behavior.pdf
大小:53KB
下载:下载


关于这三个概念, 在C-A reference manual中怎么解释, 不幸的是, 我搜遍了这本书的PDF文件, 找到的多处unspecified, 都不是解释这个概念本身的. 也许这本书中没有对此的解释.

现在, 除了C-A reference manual一书, 我们还有The New C Standard(这本电子书可以在网上免费下载.), 一本逐句解释ANSI C标准, 长度超1600页的大部头经典.

读了相关的解释, 果然受用, 整个事态逐渐明朗了.

这本书的注释澄清了下面的几个问题:
我一开始以为的 implement-defined行为是三者之中最稳定可靠的, 这种想法是不对的, unspecified行为有多种原因, 一种原因是虽然某些行为的细节是unspecified的, 但整个程序的输出对于所有可能的unspecified行为却可能不受影响, 有些行为如果要明确指定, 则需要描述当时的上下文环境, 控制流的分析, 表达式的树结构, 代码优化算法, 所以虽然理论上可以对推测出象求值顺序这样问题, 但这么做是很不现实的.

另一个澄清的问题是, unspecified与undefined的一个重要区别是, 前者是针对合法的程序, 后者则是针对非法的程序.

The New C standard, 名字有些误导, 它不是在定义另一个新的C语言标准, 而是全景式地解读既有的C99标准. 强烈推荐给拿C当回事的程序员放在手边参考. 个人认为有了这本书, 普通人读ANSI C标准不再是那么痛苦的事了.
阅读(1160) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~