5.2 实例二
问这段代码在debug版本下x86平台下,为什么是死循环?
#include <stdio.h> int main() { int i = 0; int arr[10] = { 0 }; for (i = 0; i <= 12; i++) { arr[i] = 0; printf("hehe\n"); } return 0; }
下面我们来通过调试来解释这个为什么?
真有这么巧吗?
其实并不是在VC6.0中arr[9]和i变量中间其实没有多余空间,在gcc编译器下arr[9]和i变量中间有一个整型的空间,visual studio 2022\2019\2013中的x86平台下arr[9]和i变量之间是由两个整型的空间。这些都不是巧合,而是编辑器本身规定的,不同的编译器有不同的规定。
6.如何写好优秀代码
6.1 优秀的代码
1.代码运行正常
2.bug很少
3.效率高
4.可维护性高
5.可读性高
6.注释清晰
7.文档齐全
6.2 用例题示范
模拟实现库函数:strcpy
6.2 用例题示范
模拟实现库函数:strcpy
6.2.1 方法一:按照思路实现
void my_strcpy(char* dest, char* src) { while (*src != '\0') { *dest++ = *src++; } *dest = *src; } int main() { char arr1[20] = "xxxxxxxxxx"; //xxxxxxxxxx char arr2[] = "hello"; my_strcpy(arr1, arr2); printf("%s\n", arr1); return 0; }
6.2.2 进一步优化
void my_strcpy(char* dest, char* src) { //停止条件就是'\0','\0'对应的ASCII码值是0 while (*dest++ = *src++) { ; } } int main() { char arr1[20] = "xxxxxxxxxx"; //xxxxxxxxxx char arr2[] = "hello"; my_strcpy(arr1, arr2); printf("%s\n", arr1); return 0; }
6.2.3 用断言assert库函数进一步优化
对assert函数介绍:
简单来说,assert库函数就是为了保证指针的有效性。
void my_strcpy(char* dest, char* src) { assert(dest != NULL);//断言 assert(src != NULL);//断言 //NULL是#define NULL ((void *)0) while (*dest++ = *src++) { ; } } int main() { char arr1[20] = "xxxxxxxxxx"; char arr2[] = "hello"; my_strcpy(arr1, arr2); printf("%s\n", arr1); return 0; }
6.2.4 再一步优化
void my_strcpy(char* dest, char* src) { assert(dest&&src); //NULL是#define NULL ((void *)0) while (*dest++ = *src++) { ; } } int main() { char arr1[20] = "xxxxxxxxxx"; char arr2[] = "hello"; my_strcpy(arr1, arr2); printf("%s\n", arr1); return 0; }
6.2.5 最后一步完善优化
//strcpy有返回类型,源头数组不可变这些要求 char* my_strcpy(char* dest, const char* src) { char* ret = dest; assert(dest && src);//断言 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "xxxxxxxxxx"; char arr2[] = "hello"; printf("%s\n", my_strcpy(arr1, arr2)); return 0; }
这就是调试后慢慢对细节的慢慢优化的过程。过程比结果重要,所以重在把握过程细节。
完善优化中const关键字再次理解
首先我们要知道const的作用:
告知程序员这里不能修改,具有“自描述”意思
告知编辑器这里不能修改,如果修复就会报错
1.const关键字修饰变量情景
这里的const修饰的是变量,所以这个变量是不能被修改的。但是可以用指针间接修改,如:
为什么呢?
可以这样理解,因为const只是修饰了变量a,所以不能直接用a来修改,但是可以找到a的地址进行修改,不是直接修改a,而是间接修改,下面看const关键字修是指针的时候思路会更加清晰。
2.const关键字修饰数组操作数请景
这里的const修饰变量有常属性,但是这个常属性的意思是不能被修改的意思,而非真正的把变量变成常量,这是不可能的。
3.const关键字修饰指针情景
再来聊聊指针,int * p,这里的p是指针变量用来存放地址,*是指针的标志,int是指针的类型(决定了指针访问时访问多少字节以及指针的步长问题)
3.1 const int *p
3.2 int const *p
这里int const *p和上述const int p是一样的,const都是修饰的p。
3.3 int* const p
3.4 const int* const p
4.const关键字修饰函数返回值情景
const int* test() { static int a = 10; return &a; } int main() { int* p = test(); return 0; }
这里的const修饰函数返回值的意义就是提示程序员后期维护中,不要对其返回值进行更改。
6.3 实现模拟strlen库函数
int my_strlen(const char* str) { //计数器 int count = 0; //保证指针有效性 assert(str != NULL); //判断条件再*str='\0'时停止 while (*str) { count++; str++; } return count; } int main() { const char* p = "abcdef"; int len = my_strlen(p); printf("len = %d\n", len); return 0; }
7.常见编程错误
编译型错误:直接看错提示信息
链接型错误:一般是标识符名不存在或者拼写错误
运行时错误;不容易发现的bug,一般调试才能解决
我们要做一个善于发现问题,解决问题的人,让脑袋高速飞转解决问题,这样才能成为一个厉害的人,还是要保持空杯心态,重复输入,高效输出。加油