调试(Debugging/Debug)
Debug 调试版本 不作任何优化 程序员使用
Release 发布版本 最优 用户使用
快捷键
win - >快捷键没反应需要+fn使用
mac ->command+快捷键
f5 开始调试,不会单独使用,一般和f9配合使用
f9 切换断点/(设置)取消断点
断点处右击->条件 设置一个条件断点
接下来使用f10可以逐过程依次接着从断点之后进行调试
f10 逐过程 遇到函数不进入函数,直接执行完函数的内容
f11 逐语句 比f10更加细节 遇见函数,会进入函数,执行代码的每个细节,粒度更细
int main()
{
int i = 0;
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
for (i = 0; i <= 12; i++)
{
arr[i] = 0;//下标越界访问
printf("hehe\n");//死循环
}
return 0;
}
i arr是局部变量存放在栈区
栈区的使用习惯:先使用高地址处的空间,再使用低地址处的空间。
数组随下标的增长,地址是由低到高变化的
如果在空间分配的时候i与arr之间刚好空出两个整型空间,在循环的过程中向后越界,到12的时候就是i,就会把i改掉,造成死循环。
这个代码非常依赖环境的
常见的coding技巧:
- 使用assert(断言)
- 尽量使用const
- 养成良好的编码风格
- 添加必要注释
- 避免编码的陷阱
//模拟实现库函数:strcpy
//arr2必须>arr1
#include <string.h>
int main()
{
char arr1[] = "hello bit";
char arr2[20] = { 0 };
strcpy(arr2, arr1);//把arr1中的内容拷贝到arr2当中
printf("%s\n", arr2);//会把\0也拷贝过去
return 0;
}
my_strcpy(char* dest, char* src)
{
while (*src != '\0')
{
*dest = *src;
dest++;
src++;
}
*dest = *src;
}
int main()
{
char arr1[] = "hello bit";
char arr2[20] ="xxxxxxxxxxxxxxxx";
my_strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
//优化代码
#include <assert.h>
void my_strcpy(char* dest, char* src)
{
//assert(dest != NULL);//断言
//assert(src != NULL);//断言
assert(dest && src);
while (*dest++ = *src++)
{
;
}
}
int main()
{
char arr1[] = "hello bit";
char arr2[20] = "xxxxxxxxxxxxxxxx";
char* p = NULL;
my_strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
//my_strcpy函数设计了返回值了类型,是为了实现函数的链式访问
char* my_strcpy(char* dest,const char* src)
{
assert(dest && src);
char* ret = dest;
while (*dest++ = *src++)//如果此处写反了编译器会报错,健壮性更好
{
;
}
return ret;
}
int main()
{
char arr1[] = "hello bit";
char arr2[20] = "xxxxxxxxxxxxxxxx";
char* p = NULL;
printf("%s\n", my_strcpy(arr2, arr1));
return 0;
}
const的作用:
const可以修饰指针
const放在*左边(const int* p;)
const修饰的是*p,表示p指向的对象不能通过p来改变,但是p变量中的地址是可以改变的
const放在*右边( int* const p;)
const修饰的是p,表示p的内容不能被改变,但是p指向的对象是可以通过p来改变的
int main()
{
//int num = 10;
//num = 20;
//int* p = #
//*p = 100;
const int num = 10;//被const修饰是常变量不可被修改
//num = 20;
const int *p = #//const限制的是*p
int* const p = #//const限制的是p
// *p = 100;
printf("%d\n", num);//100
return 0;
}
strcpy的缺陷:万一arr2比arr1的内存小,可能就无法拷贝
模拟实现一个strlen函数
#include <assert.h>
#include <string.h>
int my_strlen(const char* str)
{
assert(str != NULL);
int ret = 0;
while (*str++ !='\0')
{
ret++;
}
return ret;
}
int main()
{
int len = my_strlen("abcdef");
printf("%d\n",len);
return 0;
}
编译型错误(语法错误)
没有返回值错误
int main
{
return 0;
}
//编译器会提醒
直接看错误提示信息(双击),解决问题或凭经验。相对而言比较简单。
链接型错误
test.c test.exe
源文件 ——> 编译 ——>链接 可执行程序
看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是标识符不存在或拼写错误。
运行时错误
借助调试,逐步定位问题。最难搞。