最近写代码(毕业设计)时,发现自以为已经基本掌握C/C++编程的我经常会犯一些比较基础和简单的错误,而往往这些错误是难以察觉的。
讽刺的是这些东西在笔试面试中的改错题中经常考,而我那时都能很快的找出来...囧rz
今天就先说说刚刚查出来的一个我找了很久的bug,这个是有关指针形参的。先介绍一下指针形参的一些使用情形。
1.简单的指针形参 (交换两个值,C风格的字符串)
对于交换两个值,这个应该是但凡是C/C++的书都要给出第一个例子:
void swap(int* a,int* b) { int tmp = *a; *a = *b; *b = tmp; }
对与C风格的字符串,一般是以[const] char* str的形式出现,我一般设计的函数是包含[const] char *str 和 size_t len两个参数来将字符串长度
的职责分配给字符串的Maker:
//count the number of char 'a' int count_a(const char* str,size_t len) { int count = 0; if(len == 0) return count; int i; for(i=0;i<len;i++) if(str[i] == 'a') count++; return count; }
2.数组作为形参(一维,多维,C++中的引用作为形参来限制数组大小(这个参见《C++Primer》吧))
数组和指针的区别在此就不细说了。但在函数调用时,编译器会将数组实参转化为指针,如:
void deal_with_array(int* array); int a[10] = {0}; deal_with_array(a);
陷阱1:多维数组呢?如:
int two_dimension[10][10]; void deal_two_dimensions(int**); //C89 and C99 will give a warning and C++98 will give a error deal_two_dimensions(two_dimansion);
这时,C会指出需要类型int** 而实参为int (*)[10],即编译器只是进行了一步的转化,而并非一次到位的。而C++在此就更加严格了,直接
报错不能通过编译了。
3.指针形参(指针本身作为形参,容易被忽视掉,而且问题不容易被发现 )
其实指针形参之前已经说过,但是在这里要提一下一个陷阱,也是我经常容易犯的。
陷阱2:交换或改变指针本身(而不是其指向的内容)。
举个简单的例子(这也是我犯过的错),如果我要用C的库函数qsort来对一组C风格的字符串排序。
例子1:
const char* array[10]; int str_compare(const void*,const void*); //nothing seems wrong qsort(array,10,sizeof(char*),str_compare); int str_compare(const void* a,const void* b) { //Is it right? const char* x = (const char*)a; const char* y = (const char*)b; //compare x and y via lexicographical return strcmp(x,y);}
上述代码有错吗?编译:OK,链接:OK,运行:OK…神马!运行结果不对,没有正确排序!
好了,不卖关子了,细心的读者应该能看问题来了。这么说吧,要排序的数组元素的类型为const char* ,而大家都知道排序操作是要改
变数组元素的位置的,也就是说要交换array中的两个const char*类型的元素,啊哈!大家应该想起了本文的第一个例子。正确的compare函
数的写法为:
//万恶的qsort所需compare函数原型
int compare_str(const void* a,const void* b) { const char** x = (const char**)a; const char** y = (const char**)b; return strcmp(*x,*y); }
这个compare函数原型中的两个const void*参数实在是太具有迷惑性了啊。
例子2:还是与字符串有关(谁叫C风格的字符串是指针呢)
需求是这样的:我要处理一个字符串,其中包含了某些结构以及其属性的文本化表示(原理类似与xml),我的函数(s)要读取
这个字符串,并生成相应的结构和填充其属性,这些函数是顺序调用的(当然是按照字符串中结构出现的次序)。我最初的设计
是这样的:
struct A read_A(char* pattern); struct B read_B(char* pattern); struct C read_C(char* pattern); ...
但是总是读到B时就出错了(我在每个read_X()中都检查类型信息)。恩,你应该知道了,是因为传入的参数是char* pattern,我在函数中做类似pattern++,pattern--时,并没有真正的移动传入的指针,而是移动的零时变量,结果可想而知,传入到read_B()和read_C()中的指针值和
read_A的相同。出现这个问题只能说明并没有真正理解指针形参的含义或者是没有时刻保持一颗警惕的心啊(这个bug找了好久,最后没办发用
debug才找出来)。
总的来说,在用指针作为形参时,要注意的是1.需求,2不要被原本是指针的参数所蒙蔽。以上是我遇到的一些问题总结和经验,希望能对大
家有用,谢谢!