C++内存问题大集合(指针问题,以及字符串拷贝问题,确实挺危险的)
作者:rendao.org,版权声明,转载必须征得同意。
内存越界,变量被篡改
memset时长度参数超出了数组长度,但memset当时并不会报错,而是操作了不应该操作的内存,导致变量被无端篡改
还可能导致内存越界的函数有memset、memcpy、memmove、strcpy、strncpy、strcat、sprintf等等
临时指针问题,std::string、wstring的c_str()是个临时指针
c_str()返回值是个char*/wchar_t*指针,这个数组的数据是临时的,当有一个改变这些数据的成员函数被调用后,其中的数据就会失效。此时,用strlen、wcslen、strcpy、wcscpy等操作都是不可预知的,程序会崩溃。因此要么现用先转换,要么把它的数据复制到用户自己可以管理的内存中。
string s=”1234″;
const char* c = s.c_str();
这样的写法是错误的。因为重复使用的c指向内容实际上很容易失效。
delete char*时程序崩溃
1、内存越界
char* newstr = new char[strlen(s1) + strlen(s2) + 1];
sprintf(newpath, “%s\\%s”, s1, s2);
然后这个newstr在被delete时崩溃了,原因是开辟的内存空间不够,内存越界,无法容纳结束符0,如果把strlen(s1) + strlen(s2) + 1改成strlen(s1) + strlen(s2) + 2就OK了。
2、删除的指针不属于自己的堆栈
如果对于堆来说,每个DLL有自己的堆,那么从DLL中动态分分配的内存最好是从DLL中删除。如果从DLL中分配内存,然后再EXE中或者另外一个DLL中删除,很可能导致程序崩溃。并且,输出报错为Invalid Address specified to RtlValidateHeap。
3、重复delete
重复delete同一个指针会崩溃,有时不易发现,可能的情况之一就是某类存在char*成员(需要new开辟内存),而此类是浅拷贝,若不同对象A、B间发生拷贝,A销毁时释放了char*指向内存,B仍认为自己的char*指针是有效的。有时现象更加隐秘,你晓得有浅拷贝这回事还不够,比如拷贝的发生不一定是你自己的代码中直接导致的,比如vector的push_back操作就会不停的导致类对象的拷贝,而你不了解push_back的本质的话就不晓得发生了拷贝。
4、不同工程使用了不同的运行时库设置
具体现象是delete指针时程序崩溃,并输出提示Invalid Address specified to RtlValidateHeap,这是因为在不同模块(工程)之间传递 C++ 类,而这两个模块用了不同的运行时库(C/C++ -> Code Generation -> Use runtime library)设置。例如:EXE 模块调用 DLL 模块里传递 C++ 类的函数,DLL模块使用静态链接(Release 是Multi-threaded (/MT) 、Debug 是 Multi-threaded Debug (/MTd) )方式编译,而 EXE 模块使用动态链接(Release 是 Multi-threaded DLL (/MD) 、Debug 是 Multi-threaded Debug DLL (/MDd) )方式编译。可以对比这两个模块的Use Runtime Library ,看看设置是否一样,如果不一样要改成一样的。
以上是网络上的解释,而且这个原因可能和第3条有关联,可能是由于运行时库的设置不同,导致调用另外模块的类的方法时,导致这时new出来的内存所属堆归属有问题,所以delete时就出错了。这一段是简单的猜测,很可能猜错了,但这个问题已经消耗了非常多时间了,抓紧,实践证明,我在EXE(Peer)和DLL(Reg)中使用了相同的类(Xstr),并都是源码加入工程编译的,EXE中新建类对象,把对象指针传入DLL,DLL中调用类的某方法(会new一段内存)来容纳返回的数据,而这个类对象在EXE中别销毁时就出现了delete wchar_t*崩溃的问题,提示Invalid Address specified to RtlValidateHeap。此时检查运行时库设置,发现的确不同,而且正如网上摘录中举得例子一样,如果把EXE和DLL中的该设置都改为Multi-threaded DLL/ Multi-threaded Debug DLL,则问题解决。 Multi-threaded与Multi-threaded DLL,前者是指使用多线程版本的运行时库但静态编译,后者的差别就是动态链接。
关于Use runtime library的含义,微软和C有两种C运行期函数库,一种是普通的函数库:LIBC.LIB,不支持多线程。另外一种是支持多线程的: msvcrt.lib。如果一个工程里,这两种函数库混合使用,可能会引起这个错误,一般情况下它需要MFC的库先于C运行期函数库被链接,因此建议使用支持多线程的msvcrt.lib。所以在使用第三方的库之前首先要知道它链接的是什么库,否则就可能造成LNK2005错误。如果不得不使用第三方的库,可以尝试按下面所说的方法修改,但不能保证一定能解决问题,前两种方法是微软提供的: A、选择VC菜单Project->Settings->Link->Catagory选择Input,再在Ignore libraries 的Edit 栏中填入你需要忽略的库,如:Nafxcwd.lib;Libcmtd.lib。然后在Object/library Modules的Edit栏中填入正确的库的顺序,这里需要你能确定什么是正确的顺序.(最好不要这么做)
B、选择VC菜单Project->Settings->Link页,然后在Project Options的Edit栏中输入/verbose:lib,这样就可以在编译链接程序过程中在输出窗口看到链接的顺序了。 C、选择VC菜单Project->Settings->C/C++页,Catagory选择Code Generation后再在User Runtime libraray中选择MultiThread DLL等其他库,逐一尝试。
5、指针所指内存地址不再是new出来的首地址
char* p = new char[10];
strcpy(p, “hello”);
p++;
delete p; //运行到这回崩溃
内存泄露的解决
1、 查找new和delete,malloc/realloc/calloc/strdup和free,VirtualAlloc和VirtualFree是否成对
2、 检查new char之后是否误用了小括号,因为使用小括号的话,是赋初值,方括号才是指定长度。Debug时可能越界执行而不立即报错,但程序在退出时则会提示内存泄露了。当然,如果是release,估计程序执行到这里将立即崩溃。
3、 有时候,VC的内存泄露提示行代码中new出来的类对象出现内存泄露,而实际上检查发现该类对象的的确确是delete了的,实际上是提示不够精确
可能1:实际发现还可能不是该类对象new了未释放,而是该类对象中的代码、包括调用的函数中出现了new但未释放的情况,比如new CmySiteRoot中调用了xml_INIeasyget,而xml_INIeasyget中调用了只new不delete的charenc_strutf8toansi,进而导致内存泄露的,但报错位置却是new CmySiteRoot
可能2:事实证明,甚至可能和报错的new位置new出来的那个东西毫不相干的,此时还是乖乖按照步骤解决方法1去查找,反而快些,基本肯定的是,按照步骤1去找,总可以找到不匹配的地方,不一定过于相信VC的输出提示位置
减少数组越界读写的方法
boost::array(debug时检查数组下标,release时效率和普通数组一样)、智能指针、vector
内存泄露、越界检查工具
BoundsCheck、pageheap、gflags(Windebug中带的)、WinDebug
字符串拷贝注意要点
一定要注意结束符,经过实际验证,strncpy、wcsncpy、memcpy、wcstombs等等函数都只会拷贝指定个数的字符,而不理会是否在末尾有结束符,如果忘记了自己补上结束符,可能导致访问这个字符串时访问到别人的内存喔。
Related Posts:
http://rendao.org/blog/224/
C++内存问题大集合(指针问题,以及字符串拷贝问题,确实挺危险的)的更多相关文章
- Com组件的内存分配和释放,CredentialProvider SHStrDup 字符串拷贝问题
一.简单介绍 熟悉CredentialProvider的同学应该知道,他为一个Com组件,于是,在这里的内存分配(字符串拷贝)的一系列操作就要依照con的标准来. 二.Com组件的内存分配和释放 CO ...
- 重磅硬核 | 一文聊透对象在 JVM 中的内存布局,以及内存对齐和压缩指针的原理及应用
欢迎关注公众号:bin的技术小屋 大家好,我是bin,又到了每周我们见面的时刻了,我的公众号在1月10号那天发布了第一篇文章<从内核角度看IO模型的演变>,在这篇文章中我们通过图解的方式以 ...
- Redis 内存优化神技,小内存保存大数据
大家好,我是「码哥」,大家可以叫我靓仔. 这次码哥跟大家分享一些优化神技,当你面试或者工作中你遇到如下问题,那就使出今天学到的绝招,一招定乾坤! 如何用更少的内存保存更多的数据? 我们应该从 Redi ...
- LINUX下编译安装PHP各种报错大集合
本文为大家整理汇总了一些linux下编译安装php各种报错大集合 ,感兴趣的同学参考下. nginx1.6.2-mysql5.5.32二进制,php安装报错解决: 123456 [root@clien ...
- Unity3D占用内存太大的解决方法
原地址:http://www.cnblogs.com/88999660/archive/2013/03/15/2961663.html 最近网友通过网站搜索Unity3D在手机及其他平台下占用内存太大 ...
- 大礼包!ANDROID内存优化(大汇总)
写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上把网上搜集的各种内存零散知识点进行汇总.挑选.简化后整理而成. 所以我将本文定义为一个工具类的文章,如果你在A ...
- ANDROID内存优化——大汇总(转)
原文作者博客:转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! ANDROID内存优化(大汇总——上) 写在最前: 本文的思路主要借鉴了20 ...
- C# 快速释放内存的大数组
本文告诉大家如何使用 Marshal 做出可以快速释放内存的大数组. 最近在做 3D ,需要不断申请一段大内存数组,然后就释放他,但是 C# 对于大内存不是立刻释放,所以就存在一定的性能问题. 在博客 ...
- (转)Unity3D占用内存太大的解决方法
自:http://www.cnblogs.com/88999660/archive/2013/03/15/2961663.html 最近网友通过网站搜索Unity3D在手机及其他平台下占用内存太大. ...
随机推荐
- maven 问题汇总
尽量用高版本maven 3以上的版本 1.maven环境变量相关 maven环境变量配置依赖于JAVA_HOME配置 maven环境变量配置为M2_HOME maven配置完环境变量需要重启操作系统 ...
- C# 同步/并发队列ConcurrentQueue
如下所示,ConcurrentQueue做到了代码的简化,在并发模型中充当同步对象 private ConcurrentQueue<string> inQueue = new Concur ...
- SGU438 The Glorious Karlutka River =)(最大流)
题目大概说有m个人要过一条宽W的河,人最远跳远距离是d,河上有n个垃圾堆,每个垃圾堆都有坐标和同一时间能容纳的人数,问所有人最少要跳几次才能跳到对岸. 又是一题根据时间拆点的最大流. 二分时间建容量网 ...
- DataTable排序的一般方法
一.重生法dstaset.Tables.Add(dt)dataset.Tables(0).DefaultView.Sort = "id desc" 二.直接法dv = New Da ...
- 我理解的 js 的观察者模式 Observable
我第一次看 四人帮 写的<设计模式>时一头雾水,现在也是,或许其是针对专业的程序员学习使用的. 通过对Ext / Backbone 源码的学习,可总结如下: 模式 - 就是对解决某一类特定 ...
- linux网络配置相关命令、虚拟网络接口eth0:0
网络接口(interface)是网络硬件设备在操作系统中的表示方法,比如网卡在Linux操作系统中用ethX,是由0开始的正整数,比如eth0.eth1...... ethX.而普通猫和ADSL的接口 ...
- redis API使用说明
List相关: LPOP key : 删除并取得LIST头部一个元素 RPOP key : 删除并取得LIST尾部一个元素 BLPOP key [key ...] timeout : 删除并取得LIS ...
- CentOS下搭建使用gitlab 和tortoiseGit使用
gitlab和github 一样很爽的一个东西 关于gitlab在CentOS下的安装方法地址参考: https://github.com/gitlabhq/gitlab-recipes/tree/m ...
- 运用正则表达式在Asp中过滤Html标签代码的四种不同方法
Function RemoveHTML(strHTML)Dim objregExp, Match, MatchesSet objRegExp = New RegexpobjRegExp.IgnoreC ...
- Swift UICollectionView 简单使用
最近要研究下排布的游戏关卡界面的实现,简单做了个UICollectionView的demo. 先看最后的效果: 下面来看实现的方法把,在Storyboard对应的ViewController中增加一个 ...