实用调试技巧

调试(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技巧:

  1. 使用assert(断言)
  2. 尽量使用const
  3. 养成良好的编码风格
  4. 添加必要注释
  5. 避免编码的陷阱

//模拟实现库函数: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 = &num;
	//*p = 100;

	const int num = 10;//被const修饰是常变量不可被修改
	//num = 20;

	const int *p = &num;//const限制的是*p
	int* const p = &num;//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

源文件           ——> 编译 ——>链接         可执行程序

看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是标识符不存在或拼写错误。

运行时错误

借助调试,逐步定位问题。最难搞。

猜你喜欢

转载自blog.csdn.net/Ll_R_lL/article/details/123263922
今日推荐