再贴多一种类似情况,对指针解引用后,操作不属于自己的内存空间(0地址),0地址是不准许
访问的!!!
一种情况是这样的,对只读空间进行修改,也是不允许的,一改就会出错!!!直接崩溃
另外一种就是使用了strcpy强行改变了常量字符串的内容
这种就比较隐蔽了。。。单步调试是一种神器
我用C++写了链表类,然后将两个链表合并为1个,简单的将第2个链表的尾指针指向了第1个链表的头指针,然后
析构了第二个链表之后,又析构了第一个合并后的链表
,造成了非常隐蔽的double free,令我十分不解,
单步调试也发现不了问题,后来直接打印所有节点的地址,
恍然大悟!!!是二次free,简直了!!!
第一张图是有问题时的情况:
释放的内存地址0083E8A8被释放了2次,第一次释放是析构输出的第一行,第二次是析构输出的最后一行
使用了非动态内存或者使用了不属于自己的内存空间
对上图的解释:你试图对随机的一个地址111进行delete,这个内存不属于你,可能不是动态内存,但是你却试图释放它,其结果是未知的,往往导致程序崩溃!!
如果你仅仅是打印出来,可能不会异常,但是,如果你试图修改不属于你的内存空间,
就一定会异常!!
注:另外常见的还有strcpy,strncpy,strcat,memset等库函数,如果第一个参数未被初始化,
程序同样崩溃
上图的解决方案:对q进行初始化,让其指向有效的空间,这里我使用了new分配内存空间
,当然你改成char p[4];也是合理的;
图示如下:
还有一种情况就是如下
char* p;//指向未知内存
scanf("%s",p);//试图修改不属于自己的空间
printf("%s",p);//如果用Clion去debug,断点会一直停在scanf那一行,
// 报告EXC_BAD_ACCESS!!!!
改正如下:
char* p = new char[100];//如果申请成功,指向有效内存
scanf("%s",p);//试图修改属于自己的空间
printf("%s",p);//ok!!
#include
using namespace std;
class Stu{
private:
char* name;
public:
Stu(const char* _name):name(new char[(std::strlen(_name)+1]){
//省略申请失败的检查
std::strcpy(name,_name);
}
};
void write(Stu* s, int n) {
ofstream out("C:\\Users\\axin\\Desktop\\b.txt", ios::out);
for (int i = 0; i < n; i++)
out.write(reinterpret_cast<char*>(s + i), sizeof(Stu));
//这个write只是把name指向的首地址,即指针本身的值写进去了文件里面
//但是会导致读取的时候出现字符串读取异常,所以read出来再cout对象的时候会
//程序崩溃!!!!
}
void read(Stu* s, int n)
{
ifstream in("C:\\Users\\axin\\Desktop\\b.txt", ios::in);
for (int i = 0; i < n; i++)
{
in.read(reinterpret_cast<char*>(s + i), sizeof(Stu));
if ((s+i)->getScore() >= 60) {
cout << s[i];//间接访问对象的name,由于读取字符串异常,访问异常的指针
//必然运行崩溃
}
}
}
int main()
{
Stu s[3] = {
Stu(20190001,"Li",99),
Stu(20190002,"Zhang",59),
Stu(20190003,"Zhao",100)
};
//write(s, 3);
//如果事先把对象写入文件,后面只是调用read读取,那么访问对象name的时候就会导致
//程序崩溃
read(s, 3);
return 0;
}
总结
这正是不能把含有string成员的对象写入文件的原因,string内部使用指针管理动态内存
我这个例子也是用动态内存管理,都会出现同样的问题!!!
Vs2019下:
有一次我写的函数忘记返回了vector,结果程序执行完最后一条代码后,异常崩溃,
后面发现编译器给了一个Warning:不是所有路径都有返回值,然后我写了个return后就
正常了
哎,就是非法内存访问错误,各种内存管理和地址的非法访问
参考文章:
百度百科-段错误
作者:杰出天下的一篇文章:段错误