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在手机及其他平台下占用内存太大. ...
随机推荐
- js:语言精髓笔记11--动态语言特性(1)
语言:程序最终被表达为数据(结构)和逻辑(算法),命令式和说明式/函数式语言分别从这两方面分类: 动态:在语言陈述时无法确定,必须在计算机执行时才能确定语言关系:JS是完全动态语言,导致其不确定性一般 ...
- ARP缓存表的构成ARP协议全面实战协议详解、攻击与防御
ARP缓存表的构成ARP协议全面实战协议详解.攻击与防御 1.4.3 ARP缓存表的构成 在局域网的任何一台主机中,都有一个ARP缓存表.该缓存表中保存中多个ARP条目.每个ARP条目都是由一个IP ...
- 使用“Empty 模式”改进 Null Object
概述 Null Object 是Martin 大师提出的一种重构手段,其思想就是通过多态(派生一个Null对象)来减少逻辑(if … then …else)的判断. 而.NET中已经有Null Obj ...
- 【BZOJ】1015: [JSOI2008]星球大战starwar(并查集)
http://www.lydsy.com/JudgeOnline/problem.php?id=1015 看了题解的囧T_T,一开始以为是求割点,但是想到割点不能统计.... 这题用并查集,思想很巧妙 ...
- MySQL 用户管理——权限表
权限表 权限表存放在mysql数据库中 user表结构 用户列:Host.User.Password 权限列:*priv 资源控制列:max* 安全列:其余 db表 存储了用户对某个数据库的操作权 ...
- Cite a Website in Paper 论文中引用网页的格式
Template: 1.A. Author Surname, 'Title', Year Published, <http://Website-Url> (accessed 10 Octo ...
- CvMat and cv::Mat
CvMat: typedef struct CvMat { int type; int step; /* for internal use only */ int* refcount; int hdr ...
- shell 循环
for循环: 批量删除.gz结尾的文件: 循环打包文件并备份到一个目录下面: find ./ -maxdepth 1 -name "*.gz" find ./ -maxdepth ...
- SparkContext.setCheckpointDir()
class SparkContext extends Logging with ExecutorAllocationClient Main entry point for Spark function ...
- #ifdef __cplusplus extern "C" { #endif //一段代码 #ifdef __cplusplus } #endif
这样的代码到底是什么意思呢?首先,__cplusplus是cpp中的自定义宏,那么定义了这个宏的话表示这是一段cpp的代码,也就是说,上面的代码的含义是:如果这是一段cpp的代码,那么加入" ...