献给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下的控制台说成是DOS
a. 控制台是一个纯正的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";

评论