6. 如何写出好(易于调试)的代码。
6.1 优秀的代码
优秀的代码应该满足以下条件。
1. 代码运行正常
2. bug很少
3. 效率高
4. 可读性高
5. 可维护性高
6. 注释清晰
7. 文档齐全
为了达到这样的条件,我们可以使用以下常见的coding技巧:
1. 使用assert
2. 尽量使用const
3. 养成良好的编码风格
4. 添加必要的注释
5. 避免编码的陷阱。
6.2 示范
我们来模拟实现库函数strcpy:
函数的参数形式char* strcpy(char*destination,const char*source);
该参数说明了strcpy返回类型是char类型的指针,将源头(不能被改)拷贝到目的地。
strcpy特点和strlen类似,遇到‘\0’就停止。
比较容易想到的写法是:
#include<stdio.h> #include<string.h> #include<assert.h> void my_strcpy(char* a, char* b) { while (*a != '\0') { *b = *a; b++; a++; } *b = *a; } int main() { char a[] = "abcdef"; char b[10]; my_strcpy(a, b); printf("%s", b); return 0; }
虽然可以实现strcpy函数的内容,但是优化不怎么样,将简单的功能写的非常麻烦,且无法避免空指针的情况,下面为优化后的写法:
#include<stdio.h> #include<assert.h> void my_strcpy(const char a[],char b[]) { assert(a!=NULL&&b!=NULL);//断言函数来避免空指针的情况 while (*b++ = *a++) { ;//当*a为\0的时候,while里的值为假,跳出循环 } } int main() { char a[] = "abcdef"; char b[10]="";//定义第二个数组来拷贝数组 my_strcpy(a,b); printf("%s", b); return 0; }
值得注意的是,assert断言函数和const的使用,可以大大增加代码的安全性。
6.3 const的作用
#include <stdio.h> //代码1 void test1() { int n = 10; int m = 20; int *p = &n; *p = 20;//ok? p = &m; //ok? } void test2() { //代码2 int n = 10; int m = 20; const int* p = &n; *p = 20;//ok? p = &m; //ok? } void test3() { int n = 10; int m = 20; int *const p = &n; *p = 20; //ok? p = &m; //ok? } int main() { //测试无cosnt的 test1(); //测试const放在*的左边 test2(); //测试const放在*的右边 test3(); return 0; }
结论:
const修饰指针变量的时候:
1. const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改 变。但是指针变量本身的内容可变。
2. const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指 针指向的内容,可以通过指针改变。
6.4. const的实例
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> int my_strlen(const char a[])//用const来使代码更加安全 { int count = 0; while (* a++ != '\0') { count++; } return count; } int main() { char a[]="abcdef"; int b = my_strlen(a); printf("%d", b); return 0; }
7.编程常见的错误
7.1 编译型错误
直接看错误提示信息(双击),解决问题。或者凭借经验就可以搞定。相对来说简单。
如:逗号的使用,分号的添加,括号的对应,各类操作符的使用,库函数的使用格式等。
对于这种问题,我们可以直接通过错误列表的提示来定位问题所在,解决问题。
7.2 链接型错误
看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是标识符名不存在或者拼写错误。
如:变量、头文件的包含,文件的引入,常量和宏的定义,库函数名的拼写,自定义函数名的一致等等。
就例如将main写错为mian这样的错误。
7.3 运行时错误
借助调试,逐步定位问题。最难搞。
如:栈溢出,逻辑漏洞,未指针的越界访,未初始化的变量,字符串溢出,数组越界,重复释放内存,使用无效的指针等等。
就像上文里的几个例子。
对于这样的问题,可以通过调试来解决。
说了这么多,调试的章节终于结束了!
希望大家都能成为20%的时间在写程序,但是80%的时间在调试的程序员!