6、如何写出好(易于调试)的代码
6.1、优秀的代码
优秀的代码应具有:1.代码运行正常
2.bug很少
3.效率高
4.可读性高
5.可维护性高
6.注释清晰
7.文档齐全
我们常见的coding技巧有:1.使用assert
2.尽量使用const
3.养成良好的编码风格
4.添加必要的注释
5.避免编码陷阱
6.2、示范
对于上述我们所说coding技巧我们将在下面这个例子进行实现
这里呢我们模拟实现库函数:strcpy
首先呢,我们先看一下这个函数的参数和返回值类型
返回值类型为char*,参数都为char*,const后续会讲到,接下来我们进行实现,先写出主函数如下:
1. //int main() 2. //{ 3. // char arr1[] = "hello bit"; 4. // char arr2[20] = "xxxxxxxxxxxxx"; 5. // printf("%s\n", my_strcpy(arr2, arr1)); 6. // return 0; 7. //}
当我们不知道使用const时我们的写法是这样的
1. //char* my_strcpy(char* dest, char * src) 2. //{ 3. // char* ret = dest;//由于后面++操做会使dest所指向的地址发生变化,所以这儿提前将首地址存入ret 4. // while (*dest = *src) 5. // { 6. // dest++; 7. // src++; 8. // } 9. // return ret;//返回ret,也就是最初的dest,方便后续printf打印 10. //}
而这个代码其实我们还可以进行优化,优化如下
1. //char* my_strcpy(char* dest, const char * src) 2. //{ 3. // char* ret = dest 4. // while (*dest++ = *src++) //进行了优化 5. // ;//空语句 6. // return ret; 7. //}
但是呢使用者不知道我们应该怎么给参数,如果使用者一不小心给了一个空指针呢比如下面
我们发现代码并没有报错,还是走了下去,但这是不是我们需要的呢?显然不是,这时候我们就需要使用前面所讲的assert(断言),搭配头文件#include <assert.h>使用
断言(assertion)是一种在程序中的一阶逻辑(如:一个结果为真或假的逻辑判断式),目的为了表示与验证软件开发者预期的结果——当程序执行到断言的位置时,对应的断言应该为真。若断言不为真时,程序会中止执行,并给出错误信息。
使用如下
1. 2. char* my_strcpy(char* dest, const char * src) 3. { 4. // //断言 5. assert(dest != NULL); 6. assert(src != NULL); 7. char* ret = dest; 8. while (*dest++ = *src++) //进行了优化 9. ;//空语句 10. return ret; 11. } 12. 13.
我们测试一下看一下使用效果
我们发现它不在不作为,而是给你报出你的代码在什么路径下哪里出现错误,我们这里显示第8行出现错误,而我们地8行接受到了空指针, 所以程序会中止执行,并给出错误信息。
当我们搞定这些之后我们又出现了问题, 有个粗心的小伙伴在实现时,把*dest++ = *src++写反了,写成了 *src++=*dest++ ,此时我们发现程序正常运行,但给出的结果时错误的,者我们该怎么解决呢?这时候就需要用到我们接下来所要讲到的const了
6.3、const的作用
const是一个C语言(ANSI C)的关键字,具有着举足轻重的地位。它限定一个变量不允许被改变,产生静态作用。使用const在一定程度上可以提高程序的安全性和可靠性。另外,在观看别人代码的时候,清晰理解const所起的作用,对理解对方的程序也有一定帮助。
const修饰的数据类型是指常类型,常类型的变量或对象的值是不能被更新的。
主要作用
1、可以定义const常量,具有不可变性
2、便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患。例如: void f(const int i) { …} 编译器就会知道i是一个常量,不允许修改;
3、可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。
4、节省时间和提高效率,编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
我们先简单看一下吧
我们发现在对n进行修改时,编译器进行报错了,因为n不能被更新
如何使用const和理解const int* 和int *const 的区别
以一个简单的代码例子来解释
1. #include <stdio.h> 2. 3. void test1() 4. { 5. int n = 10; 6. int m = 20; 7. int *p = &n; 8. *p = 20; 9. p = &m; 10. } 11. void test2() 12. { 13. int n = 10; 14. int m = 20; 15. const int* p = &n; 16. *p = 20; 17. p = &m; 18. } 19. void test3() 20. { 21. int n = 10; 22. int m = 20; 23. int *const p = &n; 24. *p = 20; 25. p = &m; 26. } 27. 28. int main() 29. { 30. //测试无cosnt的 31. test1(); 32. //测试const放在*的左边 33. test2(); 34. //测试const放在*的右边 35. test3(); 36. return 0; 37. }
我们测试后发现
test1()函数可以正常运行,而test2 ( )和test3()不可以。
test2()中的const int* p = &n;的const的作用是使指针不能改变指向当前地址的值,也就是说,此时n等于10,不能通过指针p来改变n的值。*p=20;此时这句话会报错。
test3()中的int *const p = &n;的const的作用是使指针不能改变当前所指向的地址。也就是说,此时p指向的是n的地址,而不能再改变去指向m的地址。p = &m; 这句话会报错
总结如下
则my_strcpy()函数完整实现如下
1. //char* my_strcpy(char* dest, const char * src) 2. //{ 3. // char* ret = dest; 4. // //断言 5. // assert(dest != NULL); 6. // assert(src != NULL); 7. // 8. // while (*dest++ = *src++) 9. // ;//空语句 10. // 11. // return ret; 12. //}
7、编程常见的错误
7.1、编译型错误
直接看到错误提示信息(双击),解决问题。或者凭借经验就可以搞定。相对来说简单
双击错误就可以跳到问题行
7.2、链接型错误
看错误提示信息,主演是在代码中找到错误信息中的标识符,然后定位问题所在。一般标识符不存在或者拼写错误
如上述拼写错误,我们进行编译后发现
7.3、运行时错误
借助调试,逐步定位问题,最难搞。需要慢慢积累经验。
制作不易,一键三连!!一起加油!!!