《C++编程新手错误语录》由会员分享,可在线阅读,更多相关《C++编程新手错误语录(16页珍藏版)》请在金锄头文库上搜索。
1、C/C+编程新手错误语录时间:2009-05-26 21:57:16 来源:网络 作者:未知 点击:89 次 1引言还记得当年学数学、英语都有个窍门,那就是搞个错题集。经常复习一下这个错题集,就可以避 免下次犯同样的错误。而几乎所有的程序员都是从犯错误开始的,我们也很有必要总结一下编程新手的常见错误,本文的目的在于此。1引言还记得当年学数学、英语都有个窍门,那就是搞个错题集。经常复习一下这个错题集,就可以避 免下次犯同样的错误。而几乎所有的程序员都是从犯错误开始的,我们也很有必要总结一下编程新手的常见错误,本文的目的在于此。文中所列出的都是笔者在项目 开发中接触到的新手真实的言谈,笔者学学文革
2、腔调,姑且称之为“错误语录” 。2语录() “我的程序都是对的,可结果不对”想想你的周围,是不是也有人说这样的话?如果你也曾经说过,那就此打住,不要再说这句话,因为这句话只会显示说话者的无知。既然程序都是对的,那为什么结果不对?(2 ) “程序=算法 +数据结构”如果刚刚学完 C 语言,我们说这样的话,完全可以理解,而且可以说是正确的。但是如果你是一位即将从事 C/C+编程的程序员,那么很遗憾,这个说法只能判错,殊不知,世界上还有另一种说法:程序 = 对象 + 消息“程 序=算法+数据结构”只对面向过程的语言(C)成立,而对面向对象的语言(C+ ) ,则只能表述为“程序= 对象+ 消息 ”。传
3、统的过程式编程语言以过程为中 心以算法为驱动,面向对象的编程语言则以对象为中心以消息为驱动。这里的消息是广义的,对象 A 调用了对象 B 的成员函数,可看作对象 A 给 B 发消息。(3 ) “程序编出来,运行正确就行了”运行正确的程序并不一定是好程序,程序员时刻要牢记的一条就是自己写的程序不仅是给自己看的,要让别人也能轻易地看懂。很遗憾,许多的编程新手不能清晰地驾驭软件的结构,对头文件和实现文件的概念含糊不清,写出来的程序可读性很差。C 程序采用模块化的编程思想,需合理地将一个很大的软件划分为一系列功能独立的部分合作完成系统的需求,在模块的划分上主要依据功能。模块由头文件和实现文件组成,对头
4、文件和实现文件的正确使用方法是:规则 1头文件(.h) 中是对于该模块接口的声明,接口包括该模块提供给其它模块调用的外部函数及外部全局变量,对这些变量和函数都需在.h 中文件中冠以 extern 关键字声明;规则 2 模块内的函数和全局变量需在 .c 文件开头冠以 static 关键字声明;规则 3 永远不要在.h 文件中定义变量;许多程序员对定义变量和声明变量混淆不清,定义变量和声明变量的区别在于定义会产生内存分配的操作,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从其它模块寻找外部函数和变量。如:/*模块 1 头文件: module1.h*/int a = 5; /* 在模
5、块 1 的.h 文件中定义 int a */*模块实现文件:module1 .c*/#include “module1.h” /* 在模块 1 中包含模块 1 的.h 文件 */*模块 2 实现文件: module2.c*/#include “module1.h” /* 在模块 2 中包含模块 1 的.h 文件 */*模块 2 实现文件: module3 .c*/#include “module1.h” /* 在模块 3 中包含模块 1 的.h 文件 */以上程序的结果是在模块 1、 2、3 中都定义了整型变量 a,a 在不同的模块中对应不同的地址单元,这明显不符合编写者的本意。正确的做法是:
6、/*模块 1 头文件: module1.h*/extern int a; /* 在模块 1 的.h 文件中声明 int a */*模块实现文件:module1 .c*/#include “module1.h” /* 在模块 1 中包含模块 1 的.h 文件 */int a = 5; /* 在模块 1 的.c 文件中定义 int a */*模块 2 实现文件: module2 .c*/#include “module1.h” /* 在模块 2 中包含模块 1 的.h 文件 */*模块 3 实现文件: module3 .c*/#include “module1.h” /* 在模块 3 中包含模块
7、1 的.h 文件 */这样如果模块 1、2、3 操作 a 的话,对应的是同一片内存单元。规则 4 如果要用其它模块定义的变量和函数,直接包含其头文件即可。许多程序员喜欢这样做,当他们要访问其它模块定义的变量时,他们在本模块文件开头添加这样的语句:extern int externVar;抛弃这种做法吧,只要头文件按规则 1 完成,某模块要访问其它模块中定义的全局变量时,只要包含该模块的头文件即可。(4 ) “数组名就是指针”许多程序员对数组名和指针的区别不甚明了,他们认为数组名就是指针,而实际上数组名和指针有很大区别,在使用时要进行正确区分,其区分规则如下:规则 1数组名指代一种数据结构,这种
8、数据结构就是数组;例如:char str10;char *pStr = str;cout 1)else许多时候,虽然不同的代码可实现完全相同的功能,但是给读者的感觉是完全不同的。譬如无条件循环:while(1)有的程序员这样写:for(;)这个语法没有确切表达代码的含义,我们从 for(;)看不出什么,只有弄明白 for(;)在 C/C+语言中意味着无条件循环才明白其意。而不懂 C/C+语言的读者看到 while(1)也可猜到这是一个无条件循环。(7 ) “免得麻烦,把类里面的成员函数都搞成 public 算了 ”许多人编 C+程序的时候,都碰到这样的情况,先前把某个成员函数定义成类的priv
9、ate/protected 函数,后来发现又要从外面调用这个函 数,就轻易地将成员函数改为public 类型的。甚至许多程序员为了避免访问的麻烦,干脆把自己添加的成员函数和成员变量都定义成 public 类型。殊不知,这是一种规划的失败。在类的设计阶段,我们就要很清晰地知道,这个类的成员函数中哪些是这个类的接口,哪些属于这个类内部的成员函数和变量。一般的准则是接口(public 成员)应在满足需求的前提下尽可能简单!所以不要轻易地将 private/protected 成员改为 public 成员,真正的工作应该在规划阶段完成。(8 ) “我想用 malloc”、 “我用不好 malloc”来
10、看看一个变态程序:/* xx.c:xx 模块实现文件 */ int *pInt;/* xx 模块的初始化函数 */xx_intial() pInt = ( int * ) malloc ( sizeof( int ) );./* xx 模块的其他函数(仅为举例)*/xx_otherFunction()*Int = 10;.这个程序定义了一个全局整型变量指针,在 xx 模块的初始化函数中对此指针动态申请内存,并将 pInt 指向该内存首地址,并在 xx 模块的其他函数中都使用 pInt 指针对其指向的整数进行读取和赋值。这个程序让我痛不欲生了好多天,扼腕叹息!这是我母校计算机系一位硕士的作品!作
11、者为了用上 malloc,拼命地把本来应该用一个全局整型变量摆平的程 序活活弄成一个全局整型指针并在初始化函数中“动态”申请内存,自作聪明而正好暴露自己的无知!我再也不要见到这样的程序。那么 malloc 究竟应该怎么用?笔者给出如下规则:规则 1不要为了用 malloc 而用 malloc,malloc 不是目的,而是手段;规则 2malloc 的真正内涵体现在 “动态”申请,如果程序的特性不需动态申请,请不要用 malloc;上面列举的变态程序完全不具备需要动态申请的特质,应该改为:/* xx.c:xx 模块实现文件 */ int example;/* xx 模块的初始化函数 */xx_i
12、ntial() ./* xx 模块的其他函数(仅为举例) */xx_otherFunction()example = 10;.规则 3什么样的程序具备需要动态申请内存的特质呢?包含两种情况:(1)不知道有多少要来,来了的又走了不明白?这么说吧,譬如你正在处理一个报文队列,收到的报文你都存入该队列,处理完队列头的报文后你需要取出队列头的元素。你不知道有多少报文来(因而你不知道应该用多大的报文数组) ,这些来的报文处理完后都要走(释放) ,这种情况适合用 malloc 和 free。(2)慢慢地长大譬如你在资源受限的系统中编写一文本编辑器程序,你怎么做,你需要这样定义数组吗?char str100
13、00;不,你完全不应该这么做。即使你定义了一个 10000 字节大的字符串,用户如果输入10001 个字符你的程序就完完了。这个时候适合用 malloc,因为你根本就不知道用户会输入多少字符,文本在慢慢长大,因而你也应慢慢地申请内存,用一个队列把字符串存放起来。那么是不是应该这样定义数据结构并在用户每输入一个字符的情况下 malloc 一个CharQueue 空间呢?typedef struct tagCharQueuechar ch;struct tagCharQueue *next;CharQueue;不,这样做也不对!这将使每个字符占据“1+指针长度”的开销。正确的做法是:typedef
14、 struct tagCharQueuechar str100;struct tagCharQueue *next;CharQueue;让字符以 100 为单位慢慢地走,当输入字符数达到 100 的整数倍时,申请一片 CharQueue空间。规则 4malloc 与 free 要成对出现它们是一对恩爱夫妻,malloc 少了 free 就必然会慢慢地死掉。成对出现不仅体现在有多少个 malloc 就应该有多少个 free,还体现在它们应尽量出现在同一函数里, “谁申请,就由谁释放” ,看下面的程序:char * func(void)char *p;p = (char *)malloc();if
15、(p!=NULL); /* 一系列针对 p 的操作 */return p; /*在某处调用 func(),用完 func 中动态申请的内存后将其 free*/char *q = func();free(q);上述代码违反了 malloc 和 free 的“谁申请,就由谁释放”原则,代码的耦合度大,用户在调用 func 函数时需确切知道其内部细节!正确的做法是:/* 在调用处申请内存,并传入 func 函数 */char *p=malloc();if(p!=NULL) func(p);free(p);p=NULL;/* 函数 func 则接收参数 p */void func(char *p) /
16、* 一系列针对 p 的操作 */规则 5free 后一定要置指针为 NULL,防止其成为“野”指针(9 ) “函数 add 编译生成的符号就是 add”int add(int x,int y)return x + y;float add(float x,float y)return x + y;即便是在 C 语言中,add 函数被多数 C 编译器编译后在符号库中的名字也不是 add,而是_add。而在 C+编译器中,int add(int x,int y)会编译成类似_add_int_int 这样的名字(称为“mangled name”) ,float add(float x,float y)则被编译成_add_float _float,mangled name 包含了函数名、函数参数数量及类型信息,C+依靠这种机制来实现函数重载。所以,在 C+