回首页 回首页 ◎ 设为首页  
◎ 收藏本站  
◎ 给我留言  
  
  首 页  C/C++教程  C++之父的FAQ  C/C++动向  C/C++源代码  C/C++误区  Unix/Linux  下载中心  乱七八糟  蚂蚁的Blog  
  当前位置:首 页 >> C++之父的FAQ >> C++ 风格与技术 FAQ >> [翻译]为何C++里有些东西是未定义的?
最 近 更 新
[转] 我该把const写在类..
[翻译] 我应该使用按值..
[翻译] 你如何命名变量..
[转] 何种代码布局风格..
[转] “int* p;”和“in..
[翻译] “cout”怎么念?
[转] 宏有什么不好吗?推荐
[翻译] static_cast 有..推荐
[翻译]为何C++里有些东..推荐
[翻译] i++ + i++ 的值..推荐
最 新 推 荐
[转] 宏有什么不好吗?推荐
[翻译] static_cast 有..推荐
[翻译]为何C++里有些东..推荐
[翻译] i++ + i++ 的值..推荐
[翻译] 数组有何不好之..推荐
[转] 为何delete操作不..推荐
[转] 有placement delet..推荐
[翻译] “new”和“mall..推荐
[转] C和C++风格的内存..推荐
[翻译] 为何标准容器效..推荐
热 门 排 行
[转] 我可以写“void ma..
[翻译] 什么是纯虚函数?
[转] 这个简单的程序…..推荐
[转] 我如何从标准输入..
[翻译] i++ + i++ 的值..推荐
[转] 我该把const写在类..
[转] 我怎样才能把整数..
[转] 我应该怎样处理内..推荐
[转] 宏有什么不好吗?推荐
[翻译] static_cast 有..推荐
站 内 搜 索

Web stdcpp.cn
关键词

搜索方式

搜索范围

精确匹配
广 告

[翻译]为何C++里有些东西是未定义的?


来源:蚂蚁的 C/C++ 标准编程 作者:Bjarne Stroustrup 翻译:antigloss 等级:精品
发布于2007-09-08 10:30 被读597次 【字体:

转载请注明作者 Bjarne Stroustrup 翻译:antigloss 和出处。好吗?谢谢!

    因为机器的不同以及 C 里面也有很多未定义的东西。ISO C++ 标准里有以下术语的详细定义:“未定义”、“未指明(unspecified)”、“由实现定义”,以及“合乎语法的(well-formed)”。注意,这些术语的含义和 ISO C 标准里的定义不太相同,而且也和它们常见的用法不同。假若没有察觉到不同的人对这些术语的认识会有所偏差,讨论问题的时候常常会极度混乱。

    这是一个正确的答案,虽然可能不尽人意。和 C 一样,C++ 力图榨干硬件的每一滴血。这就是说,C++ 必须使用各种特定机器的“自然”方式来和硬件实体(位、字节、字、地址、整数计算,以及浮点数计算等)打交道,而不是我们想怎么搞就怎么搞。注意,很多被人们称为“未定义”的“东西”,事实上都是“由实现定义”的,所以只要了解我们正在使用的机器,就可以编写出完美的专门代码。整数的大小以及浮点数的取整行为正是如此。

    下面这个关于未定义行为的例子可能是最广为人知且臭名昭彰的:

                int a[10];
                a[100] = 0; // 范围错误
                int* p = a;
                // ...
                p[100] = 0; // 范围错误(除非赋值之前,p 已经指向了另一段足够大的内存空间)

    C++(和 C)中数组和指针的概念是对机器中内存和地址概念的直接表述,所以没有任何额外开销。指针的基本操作直接被映射成机器指令,不会进行范围检测。进行范围检测会影响运行时效率以及生成代码的大小。C 是被设计来编写操作系统的,要和汇编代码拼速度,所以这么决定(不检测范围)是必须的。同样,和 C++ 不同的是,即使编译器生成了检测错误的代码,C 也没有报告错误的合适的方法:C 没有异常。C++ 跟随 C 是为了与之兼容以及直接和汇编竞赛(在 OS、嵌入式系统以及数值计算领域)。如果你需要范围检测,可用一个合适的带检测的类(vector、智能指针、string 等)。好的编译器可在编译时捕捉到 a[100] 越界了,然而,要判定 p[100] 是否越界就要困难得多。一般来说,在编译时是不可能捕捉到所有范围错误的。

    其它关于未定义行为的例子起源于编译模型。编译器不能检测到各个单独的编译单元里,对象或者函数的定义是否不一致。例如:

                // file1.c:
                struct S { int x,y; };
                int f(struct S* p) { return p->x; }

                // file2.c:
                struct S { int y,x; }
                int main()
                {
                    struct S s;
                    s.x = 1;
                    int x = f(&s); // x!=ss.x !!
                    return 2;
                }

    在 C 和 C++ 里,编译 file1.c 和 file2.c 后,将它们链接成为同一个程序是非法的。链接器应该能捕捉到 S 的定义不一致,但它没有必须这么做的义务(大多数编译器都不捕捉)。很多情况下,很难捕捉各个单独的编译单元之间的不一致性。确保使用头文件的一致性有助于最大限度地减少这种问题。链接器也有正在不断改善的好兆头。注意,C++ 链接器捕捉几乎所有和函数声明不一致有关的错误。

    最后,我们来看一些非常恼人的表达式的未定义行为(很明显,应该对这些行为进行定义)。例如:

                void out1() { cout << 1; }
                void out2() { cout << 2; }

                int main()
                {
                    int i = 10;
                    int j = ++i + i++; // j 的值未定义
                    f(out1(),out2()); // 输出 12 或者 21
                }

    j 的值是未定义的,这是为了允许编译器生成最优化的代码。据称,和确保“平常地从左到右进行求值”相比,让编译器拥有求值顺序的自由这种做法能生成明显高效的多的代码。我不这么认为,但目前无数的编译器都利用了这种自由,而且有不少人热烈地为这种自由呐喊,所以要改变它并非易事,而且可能需要数十年的时间才能被整个 C 和 C++ 世界的人接受。我很失望,并非所有编译器都能为类似 ++i+i++ 这样的代码发出警告。类似地,参数的求值顺序也是未指明的。

    我觉得,未定义、未指明或者由实现定义等等的“东西”实在是太多了。然而,这说起来容易,甚至也很容易给出这样的例子,但是要修正却太难了。不过,避免这些问题从而编写出可移植的代码也并非什么难事。

原文地址http://www.research.att.com/~bs/bs_faq2.html#undefined

本文版权归 蚂蚁的 C/C++ 标准编程 以及 作者 Bjarne Stroustrup 翻译:antigloss 共同所有,转载请注明原作者和出处。谢谢。



相关专题:C++ 之父的言论
[转] 我该把const写在类型前面还是后面?
[翻译] 我应该使用按值传递还是按引用传递?

上一篇:[翻译] i++ + i++ 的值是多少?
下一篇:[翻译] static_cast 有什么好处?

共有评论 0 条 网友评分 1分 查看全部评论

查看全部评论

【发表评论】 评分:1分 2分 3分 4分 5分


验证码:

Powered By Www.Xydw.COM Ver1.14 管理
Copyright © 2005-2006 蚂蚁的 C/C++ 标准编程 All Right Reserved. XCMS
粤ICP备06014124号   站长:Antigloss