C++常见崩溃问题分析处理

C++常见崩溃问题分析处理

使用Visual Studio 2013进行c++编码的时候经常会遇到一些编译不通过或者编译通过了链接出错,好不容易运行了又出现崩溃的问题,第一种问题编译器会自动检测出来,这种只需要仔细看错误描述就能知道问题出在哪,最麻烦的就是运行中崩溃了。
我整理了一些c++常见的错误,并进行了分析,给出了解决方案。(记录下来以便后面解决问题)

1、编译链接错误

(1)error C1083:很明显可以看出是找不到该头文件;
解决方案:去编译环境(debug/release)查看是否有该头文件,添加上头文件即可解决;
在这里插入图片描述
(2)error LNK2001:无法解析的外部符号,目前发现有三种可导致该错误
① 在.h文件中定义了静态变量num,却没有在.cpp文件中进行显式初始化;
解决方式:在.cpp文件中加上int CMainWnd::num=0;
在这里插入图片描述
② 在使用动态库时候,没有包含相应的lib;
解决方案:#pragma comment( lib,“xxxx.lib”);
在这里插入图片描述
③ 函数只在类中声明了而没有进行定义;
解决方案:为声明的函数添加函数实现;
在这里插入图片描述
(3)error MSB8020:移植项目的时候经常会出现这个问题,描述是平台工具v141找不到;
解决方案:右键项目属性-配置属性-常规-平台工具集下将工具改成自己的即可解决(通过下图可以很清楚的看到问题,v141未安装,需要选择自己常用的平台工具);
在这里插入图片描述
在这里插入图片描述
(4)error C1189:提示信息是使用/md[d](crt dll版本)生成MFC应用程序需要MFC共享dll版本;
解决方案一:右键项目属性-配置属性-常规-MFC的使用改为在共享dll中使用MFC;
解决方案二:右键项目属性-配置属性-C/C+±代码生成-运行库改为多线程调试(/MTd);

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(5)error C2146,error C4430:提示信息是缺少“ ; ”,可是你会发现无论怎么看这个第9行的代码都没有问题,此刻就需要关注这个类型PCMainWnd,右键查找定义,会发现它其实被定义在.cpp文件中,却在.h中使用就会出现这种错误;
解决方案:如果必须在.h中使用这个类型,就先声明这个类,再定义这个类型PCMainWnd,即可编译通过(具体修改看下方代码);
在这里插入图片描述

class CMainWnd;
typedef CMainWnd *PCMainWnd;

class CMakePNG
{
private:
	PCMainWnd m_pWnd;
};

2、运行崩溃

上面列举了几个编译链接出错的问题,接下来看一些运行崩溃的问题,今天先说一下访问内存出错。访问内存出错的几个常见问题如下表:

内存访问出错原因
数组越界下标为负数或大于数组的长度
指针越界超出指针分配的范围
字符串越界字符串结束符不存在或目标字符串缓冲区小于源字符串
访问野指针内存已被回收后又使用这个指针,会造成无法预估的错误
访问空指针指针所指地址为NULL

(1)数组越界,不一定报错,但是数据会被破坏,还有可能造成死循环

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int nCmdShow)
{
	int i = 0;
	int arr[10] = { 0 };
	for (i = 0; i <= 15; i++)
	{
		arr[i] = 0;
	}
	return 0;
}

上面这段代码就会造成死循环,局部变量都在栈上存储,先定义的i在高地址,数组的地址是随着小标的增长而增长,后定义的arr数组,&arr[9]是数组的最后一个地址,当数组越界到合适的时候就到了i的位置,会存在&arr[x]=&i;将arr[x]改为0的同时,i值也变为0,会再次从i=0进行循环,就造成了死循环。
解决方案:将i定义为静态变量的时候,就会解除死循环,因为静态变量存在于静态区,数组再越界也不会改变i的值。但是平时编码的时候一定要注意越界这种问题,如果代码量大的话,你怎么都不会想到越界改变了哪里的数值,会导致bug出现。

(2)指针越界,和数组越界一样,不一定会报错,原因是如果越界的地址还在自己的进程里就不会报错,如果越界到其他进程,而且此时指针所指的地址正在被使用,就会出现read only之类的错误,没被使用的话还会篡改数据,也会造成无法预估的错误。

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int nCmdShow)
{
	int a[10];
	int *p = a + 9;
	++p; 
	++p;
	return 0;
}

(3)字符串越界,没有 ‘\0’ 结尾的只能称为字符数组,字符串的标志是 ‘\0’ 结尾,但是不防止误将字符数组当作字符串处理,以\0为循环结束条件,这样就会出现死循环,除非越界到一个值为0的地址上才能跳出循环。

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int nCmdShow)
{
	char arr[3] = {'s','a','a'};
	char c;
	for (int i = 0; arr[i] != '\0'; i++)
	{
		c = arr[i];
	}
	return 0;
}

还有一种字符串越界是复制字符串fromStr到toStr时,toStr的大小不够存储需要存储的数据元素时,就会出现字符串越界。

bool copyStr(char *toStr,char *fromStr)
{
	while (*fromStr != '\0')
	{
		*toStr = *fromStr;
		toStr++;
		fromStr++;
	}
	return true;
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int nCmdShow)
{
	char str1[] = "jdgkhskj";
	char str2[] = "hjh";
	copyStr(str2, str1);
	return 0;
}

上面的代码会造成str2复制到str1的前4个字符,且str2变成字符数组(str2的\0被重写了),如果继续把str2的数据当作str1使用就会造成数据不对的bug,如果再次把str2当作字符串去使用,还会造成上面字符串越界的第一种问题。

(4)访问野指针或空指针
new了一个Point对象,在fun函数中delete且置空指针之后再使用就会出现下面的崩溃(一般delete之后都会置空防止产生野指针)
解决方案:如果不是异步函数,可以在创建对象的函数里使用完之后再delete并置空,异步函数的话,在异步函数里delete并置空(但是调用之后就不能再使用这个对象了)在这里插入图片描述

//直接调用
void fun(Point* p)
{
	p->X = 2;
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int nCmdShow)
{
	Point *pt = new Point;
	pt->X = 1;
	fun(pt);
	pt->Y = 3;
	delete pt;
	pt = NULL;
	return 0;
}
//异步函数调用
void CMainWnd::fun(LPARAM lParam)
{
	Point *pt = (Point *)lParam;
	int x = pt->X;
	int y = pt->Y;
	delete pt;
	pt = NULL;
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int nCmdShow)
{
	Point *pt = new Point;
	pt->X = 1;
	PostMessage(g_MainHwnd, WM_Main_MSG, FunCb, (LPARAM)pt);
	//此处不能delete
	return 0;
}

c++编译链接运行的崩溃点暂时整理到这里,后续遇到还会持续更新。

注释
[1]: https://blog.csdn.net/Code_beeps/article/details/83243342
[2]: https://www.cnblogs.com/zhoug2020/p/6025388.html

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值