6. 如何写出好(易于调试)的代码
6.1 优秀的代码
代码运行正常
bug很少
效率高
可读性高
可维护性高
注释清晰
文档齐全
常见的coding技巧:
使用assert
尽量使用const
养成良好的编码风格
添加必要的注释
避免编码的陷阱
6.2 利用模拟库函数strcpy示范
模拟实现库函数:strcpy
库函数strcpy本身的样子
#include <stdio.h> #include <string.h> int main() { char arr1[] = "hello world"; char arr2[20] = { 0 }; strcpy(arr2, arr1); printf("%s\n", arr2); return 0; }
模拟实现库函数:strcpy
//模拟实现库函数:strcpy //strcpy // string copy //字符串拷贝 //1 void my_strcpy(char* dest, char* src) { while (*src != '\0') { *dest = *src; dest++; src++; } *dest = *src; //\0的拷贝 } int main() { char arr1[] = "hello world"; char arr2[20] = { 0 }; my_strcpy(arr2, arr1); printf("%s\n", arr2); return 0; }
图片讲解:
优化代码:
//2 void my_strcpy(char* dest, char* src) { while (*src != '\0') { *dest++ = *src++; } *dest = *src; //\0的拷贝 } int main() { char arr1[] = "hello world"; char arr2[20] = { 0 }; my_strcpy(arr2, arr1); printf("%s\n", arr2); return 0; } //3 void my_strcpy(char* dest, char* src) { while (*dest++ = *src++) { ; } //此时不需要\0的拷贝 } int main() { char arr1[] = "hello world"; char arr2[20] = { 0 }; my_strcpy(arr2, arr1); printf("%s\n", arr2); return 0; } //4 #include <assert.h> void my_strcpy(char* dest, char* src) { //断言-对程序员自己是一件非常好的习惯,出错误会告诉在哪里 //需要包含头文件<assert.h> assert(dest != NULL); assert(src != NULL); //or assert( dest && src ); while (*dest++ = *src++) { ; } } int main() { char arr1[] = "hello world"; char arr2[20] = { 0 }; my_strcpy(arr2, arr1); printf("%s\n", arr2); return 0; }
注意:
分析参数的设计(命名,类型),返回值类型的设计
这里讲解野指针,空指针的危害。
assert的使用,这里介绍assert的作用
参数部分 const 的使用,这里讲解const修饰指针的作用
注释的添加
6.3 const的作用
int main() { //int n = 10; //n = 20; int n = 100; const int m = 0; //m = 20;//err //const 修饰指针 //1. const 放在*的左边, *p不能改了,也就是p指向的内容,不能通过p来改变了。但是p是可以改变的,p可以指向其他的变量 //2. const 放在*的右边,限制的是p,p不能改变,但是p指向的内容*p,是可以通过p来改变的 // const int * p = &m; *p = 20;//err p = &n;//ok int* const p = &m; *p = 20;//ok p = &n;//err printf("%d\n", m); return 0; }
结论:
const修饰指针变量的时候:
const 放在*的左边, *p不能改了,也就是p指向的内容,不能通过p来改变了。但是p是可以改变的,p可以指向其他的变量
const 放在的右边,限制的是p,p不能改变,但是p指向的内容p,是可以通过p来改变的
图解:
const 放在*的左边
const 放在*的右边
const 放在*的两边
知道这个的意义是为什么?
回过头来看:
#include <assert.h> void my_strcpy(char* dest, const char* src) { //断言-对程序员自己是一件非常好的习惯,出错误会告诉在哪里 //需要包含头文件<assert.h> assert(dest != NULL); assert(src != NULL); //or assert( dest && src ); while (*src++ = *dest++) //加const后,如果在打代码的过程中,交换对象写反后会产生报错 { ; } } int main() { char arr1[] = "hello world"; char arr2[20] = { 0 }; my_strcpy(arr2, arr1); printf("%s\n", arr2); return 0; }
加const后,如果在打代码的过程中,交换对象写反后会产生报错,会提高代码的健壮性。
报错提醒如下:
将代码优化一下:
//strcpy函数返回的是目标空间的起始地址 char* my_strcpy(char* dest, const char* src) { //断言 - 保证指针的有效性 assert(dest && src); char* ret = dest; //把src指向的字符串拷贝到dest指向是的数组空间,包括\0字符 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[] = "hello world"; char arr2[20] = { 0 }; //链式访问 printf("%s\n", my_strcpy(arr2, arr1)); return 0; }
练习:
模拟实现一个strlen函数
库函数strcpy本身的样子
#include <string.h> int main() { int len = strlen("abc"); printf("%d\n", len); return 0; }
模拟实现:
int my_strlen(char* str) { int count = 0; while (*str != '\0') { count++; str++; } return count; } int main() { int len = my_strlen("abc"); printf("%d\n", len); return 0; }
由上面内容,直接优化
#include <stdio.h> #include <assert.h> int my_strlen(const char* str) { int count = 0; //assert(str != NULL); assert(str); //while (*str != '\0') while (*str) { count++; str++; } return count; } int main() { int len = my_strlen("abc"); printf("%d\n", len); return 0; }
7. 编程常见的错误
7.1 编译型错误
ctrl+f - 搜索
直接看错误提示信息(双击),解决问题。或者凭借经验就可以搞定。相对来说简单。
7.2 链接型错误
看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是标识符名不存在或者拼写错误。
7.3 运行时错误
借助调试,逐步定位问题。最难搞。
温馨提示:
做一个有心人,积累排错经验。
讲解重点:
介绍每种错误怎么产生,出现之后如何解决。
如果这份博客对大家有帮助,希望各位给恒川一个免费的点赞作为鼓励,并评论收藏一下,谢谢大家!!!
制作不易,如果大家有什么疑问或给恒川的意见,欢迎评论区留言。