作者: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等等函数都只会拷贝指定个数的字符,而不理会是否在末尾有结束符,如果忘记了自己补上结束符,可能导致访问这个字符串时访问到别人的内存喔。

Tags: C++内存

http://rendao.org/blog/224/

C++内存问题大集合(指针问题,以及字符串拷贝问题,确实挺危险的)的更多相关文章

  1. Com组件的内存分配和释放,CredentialProvider SHStrDup 字符串拷贝问题

    一.简单介绍 熟悉CredentialProvider的同学应该知道,他为一个Com组件,于是,在这里的内存分配(字符串拷贝)的一系列操作就要依照con的标准来. 二.Com组件的内存分配和释放 CO ...

  2. 重磅硬核 | 一文聊透对象在 JVM 中的内存布局,以及内存对齐和压缩指针的原理及应用

    欢迎关注公众号:bin的技术小屋 大家好,我是bin,又到了每周我们见面的时刻了,我的公众号在1月10号那天发布了第一篇文章<从内核角度看IO模型的演变>,在这篇文章中我们通过图解的方式以 ...

  3. Redis 内存优化神技,小内存保存大数据

    大家好,我是「码哥」,大家可以叫我靓仔. 这次码哥跟大家分享一些优化神技,当你面试或者工作中你遇到如下问题,那就使出今天学到的绝招,一招定乾坤! 如何用更少的内存保存更多的数据? 我们应该从 Redi ...

  4. LINUX下编译安装PHP各种报错大集合

    本文为大家整理汇总了一些linux下编译安装php各种报错大集合 ,感兴趣的同学参考下. nginx1.6.2-mysql5.5.32二进制,php安装报错解决: 123456 [root@clien ...

  5. Unity3D占用内存太大的解决方法

    原地址:http://www.cnblogs.com/88999660/archive/2013/03/15/2961663.html 最近网友通过网站搜索Unity3D在手机及其他平台下占用内存太大 ...

  6. 大礼包!ANDROID内存优化(大汇总)

    写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上把网上搜集的各种内存零散知识点进行汇总.挑选.简化后整理而成. 所以我将本文定义为一个工具类的文章,如果你在A ...

  7. ANDROID内存优化——大汇总(转)

    原文作者博客:转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! ANDROID内存优化(大汇总——上) 写在最前: 本文的思路主要借鉴了20 ...

  8. C# 快速释放内存的大数组

    本文告诉大家如何使用 Marshal 做出可以快速释放内存的大数组. 最近在做 3D ,需要不断申请一段大内存数组,然后就释放他,但是 C# 对于大内存不是立刻释放,所以就存在一定的性能问题. 在博客 ...

  9. (转)Unity3D占用内存太大的解决方法

    自:http://www.cnblogs.com/88999660/archive/2013/03/15/2961663.html 最近网友通过网站搜索Unity3D在手机及其他平台下占用内存太大.  ...

随机推荐

  1. 1076 K尾相等数

    时间限制:500MS  内存限制:65536K提交次数:251 通过次数:80 题型: 编程题   语言: C++;C Description 从键盘输入一个自然数K(99999999>K> ...

  2. Ubuntu 安装Redis体验

      背景:由于之前一直没有试过Linux的环境,今天加了内存之后,虚拟机开了3G,速度大大提高,对照博客试一下安装Redis的过程.   体验: 下载源码,解压,编译 $ wget http://do ...

  3. Sql不区分大小写查询

    select a.* from Pair_User  a where 1=1   and   UPPER(a.UserID) like 'EMH1001%' collate Chinese_PRC_C ...

  4. Fengshui-[SZU_B40]

    Description Fengshui is an ancient subject in Chinese tradition. Someone considers it as science and ...

  5. BZOJ3790 : 神奇项链

    Manacher求出所有极长回文子串后,得到一堆线段,转化成线段覆盖问题 预处理出g[i]表示左端点不超过i的右端点的最大值 贪心地线段覆盖即可 时间复杂度$O(n)$ #include<cst ...

  6. Lambda表达式可以被转换为委托类型

    void Main() { //向Users类中增加两人; List<Users> user=new List<Users>{ new Users{ID=1,Name=&quo ...

  7. [Unity2D]实现背景的移动

    在游戏中通常会实现的效果是玩家主角移动的时候,背景也可以跟着移动,要实现这种效果其实就是获取主角的位置,然后再改变摄像机的位置就可以了,这就需要通过脚本来实现.这个脚本添加到摄像机的GameObjec ...

  8. get和post方法的区别

    在form表单提交数据的过程中,method属性提供了两个值:get,post,默认为get方式[参1] 即一种为get提交,一种是post提交.那么这两种提交方式有什么不同呢? 查询了一些资料后,总 ...

  9. C#关闭word进程

    C#关闭word进程 foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcessesByName(&q ...

  10. Java中实现文件上传下载的三种解决方案

    第一点:Java代码实现文件上传 FormFile file=manform.getFile(); String newfileName = null; String newpathname=null ...