献给programfan论坛上的C/C++初学者 一. C、C++以及C#的关系1. C++虽然主要是以C的基础发展起来的一门新语言,但她不是C的替代品,不是C的升级,C++和C是兄弟关系。没有谁比谁先进的说法,更重要的一点是C和C++各自的标准委员会是独立的,最新的C++标准是C++98/2003年修订,最新的C标准是C99。因此也没有先学C再说C++的说法,也不再(注意这个"不再")有C++语法是C语法的超集的说法。 2. C++/CLI 和 C# 是微软的,它们与C和C++没有任何关系,虽然部分语法相似。但哪两种语言不相似呢?都是abc这26个字母。 二. 编译器的选择3. 不要使用TC/TC++/BC/CB等古老的编译器来学习C/C++,因为它们太古老了,不支持新的C/C++标准。不要使用CBX/VC++6.0/VC2003等对C/C++标准支持不好的编译器,虽然这些编译器适合工作,但不适合学习,因为它们中的语法陷阱很多。我推荐的编译器是 gcc/mingw。 三. 学C/C++,而不是学编译器4. 不要用""代替<>来包含系统头文件。使用 "" 则意味着告诉别人,这个文件不是标准库的,而是存放于工程目录下自己的文件。对于 #include <iostream> 按缺省理解为包含C++的标准文件iostream对于 #include "iostream" 按缺省理解为包含一个存在于当前工程目录下的文件iostream,这个自定义的iostream文件应该与标准的iostream文件不同,否则为什么不用 #include <iostream> ?事实上,更严重的是,#include "标准库" 其行为是由编译器定义的,只有 #include <标准库> 才是C/C++标准规定的标准库接口。如果你 #include "标准库" 形式可能有如下三种错误:a. 报某某文件不存在,因为C/C++标准只规定 #include <标准库> 后就可以使用相应函数,而并没有规定这个文件一定需要实际存在,所以使用 #include "标准库" 形式必然出错;b. 编译报错,理由同上,编译器可能为 #include <标准库> 做了格外工作,而绝不会为 #include "标准库" 做;c. 联接报错,理由同上。 5. 不要将main函数的返回类型定义为void,虽然有些编译器允许你这样做,但它不符合C/C++标准。不要将函数的int返回类型省略不写,在C/C++中要求编译器至少给一个警告。错误的示例:void main() {},main() {} 6. 不要把VC++中的 #include "stdafx.h" 贴出来,它是预编译头文件。我建议你取消VC的预编译功能,即使保留预编译,也无需将其贴出来,对别人是无用的。 7. [C++]不要#include <iostream.h>,不要#include <string.h>,因为它们已经被C++标准明确的废弃了,请改为 #include <iostream> 和 #include <cstring>。规则就是:a. 如果这个头文件是旧C++特有的,那么去掉.h后缀,并放入std名字空间,比如 iostream.h 变为 iostream。b. 如果这个头文件是C也有的,那么去掉.h后缀,增加一个c前缀,比如 string.h 变为 cstring、stdio.h 变为 cstdio, 等等。BTW:不要把<string.h>、<cstring>、<string>三个头文件搞混淆BTW:io.h/windows.h等不是C/C++的标准文件,因此它的命名C/C++不管。 8. 不要在同一条语句中包含一个变量的多个++/--,因为它们的解析在C/C++标准中没有规定,完全取决于编译器的个人行为。 9. C/C++是平台无关性语言,因此系统相关的process/GUI等不在标准C/C++库中。比如 graphics.h和windows.h等是由某个编译器提供的,而不是由C/C++提供的,而且它们不可以移植。 四. 注意语言差异和版本差异10. 不要再写 char* p = "XXX" 这种语句,要写成 const char* p = "XXX",编译器之所以让前者通过编译是为了兼容以前的大量的旧代码。BTW:const TYPE* p 和 TYPE const* p 是一样的,风格不同而已。BTW:C语言中也有const关键字。 11. sizeof('a')在C和C++上的不同,const在C和C++上的不同…… 五. 理解语言的本性12. C/C++只是语言,而且是平台无关性语言。论坛上有部分人甚至认为C就是dos,C++就是windows,那么请问linux是什么? 13. [C++]不要侮辱C++,把C++说成是面向对象的语言。面向对象曾经是设计c with classes(C++的前身)的主要目的,但C++不是,C++是一个多典范语言。主要支持过程调用、基于对象、面向对象、泛式编程 这四种编程典范,当然还支持functional, generative,meta programming等典范。 14. 语法学家不是文学家,所以当你学会了一门计算机语言时,你还需要学习数据机构和算法,还需要掌握工具和平台API的用法。 六:对那些认为C/C++太复杂 或 太简单的人 言15. C/C++是通用语言,因此语法很复杂,你应当裁减成适合你自己的语法集合,比如裁减成better C和ADT。 16. C/C++是通用语言,因此只含通用的库,你应该丰富自己需要的库,比如汽车工业协会有自己的C/C++函数/类/模板库。 七:其他17. 某些人把windows下的控制台说成是DOSa. 控制台是一个纯正的win32程序,和DOS没有关系b. 使用DOS,或使用文本界面并不使你丢脸。如果把外貌看得比内涵更重要的话,C/C++并不适合你。 18. Dev-cpp调试时为什么界面一闪而过程序结束当然是自动关闭的,这是正确的行为。(不多说) 19. 异常和错误语言本身能提供的能力最多就是捕获异常,程序的一切行为都基于程序的正确运行,而错误,这种破坏程序运行的故障,在程序(语言)本身是无法解决的(除非你使用更高一级所提供的能力,这是题外话,不谈)。所以,程序本身的异常,程序能捕获。而错误,比如内存存取拒绝,那只有操作系统有能力。 20. 模板分离编译模板可以理解为产生源代码的模板,连源代码尚未有的话,如何编译?比如对于一个xxx.cpp所包含的模板,编译器如何知道在yyy.cpp中要使用xxx<int>还是xxx<float>? 21. type[][] 和 type**二维数组其每个成员都是一个数组,而不是一个数组的指针,或别的什么handle。所以在运行期,你永远不可能得到多维数组。因为成员的size在编译时未知。 22. type var; var=val; 和 type var=val;不要谈什么运行效率,在C++中它们是调用不同的函数。连函数都不同,还谈什么效率?(严格来讲A *b;b = new A;功能正确,但逻辑不正确,应当写成A* b = new A; 当你写成 A *b; 在逻辑上则意味着 一个未初始化的指针b有存在的逻辑意义,实际上它并没有,所以说逻辑不正确。另外,A* b = new A;是初始化,而 b = new A;是赋值操作,仅就此点而言不能算谁对谁错,但一定要知道它们的逻辑含义是不一样的。 逻辑正确则功能一定正确,功能正确逻辑未必正确。要始终在逻辑面上思考问题,否则高级语言对你而言就只是比汇编少打几个字,而没有获得高级语言逻辑抽象的精髓。这样就比较可惜,在这条歧路上走下去,你可以做到比Bjarne Stroustrup更精通C++语法,但别人依然会说你对C++一窍不通。C/C++如同一个名贵木料包装起来的宝珠,你把包装盒留下了,却把宝珠扔掉了,这叫买椟还珠。) 23. modf你会写 整数部分=(int)fval; 小数部分=fval-(int)fval;但要知道 float/double 的表示范围是大于 int 的,(int)fval这种表达式就无法在所有情况下过关。 24. feof很多人误解feof,fstream::eof(),fsstream的含义,认为文件指针到达文件尾部的话,eof就是true而事实上,eof只是设计用来当读取错误时检索出错原因用的,当文件指针到达文件尾部的话,eof未必一定为true,未必一定为true。所以eof永远不应该出现在while的条件中。一个经典的错误:for( char a; fin; ){ fin >> a; cout << a;}然后发现最后一个字符总是重复一次,正确的做法是for( char a; fin>>a; ){ cout << a;}//if( !fin.eof() ) cerr<<"非正常结束.\n";

评论