作者: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. node基础 --概念

    非阻塞IO: node.js使用了事件轮询 setTimeout是非阻塞的: 对于像http,net等原生模块中IO部分也采用了事件轮询,其本质是: 当node接受到浏览器的http请求时,底层的TC ...

  2. 数学 ACdream 1196 KIDx's Triangle

    题目传送门 /* 这道题花了好长时间AC,思路有,但是表达式少写了括号一直乱码,囧! 注意:a==0时要特判:) */ #include <cstdio> #include <alg ...

  3. HDU1247 Hat’s Words(Trie树)

    常规做法是枚举每个字符串每个位置,时间复杂度O(n*len*len),(建字典树O(n*len)). 然而我看这题第一眼想的是时间复杂度O(n*len)的算法..就是建正反两棵字典树,每个字符串跑分别 ...

  4. SVN标准命令

    SVN标准命令 范例 checkout 检出 svn  co  URL 检出app/search/news/apache/主干上最新版本到本地工作副本,可执行命令: svn co  https://s ...

  5. 推荐一个很棒的JS绘图库Flot

    Flot是Ole Laursen开发的基于JQuery的纯JavaScript实现的绘图库,Flot使用起来非常简单,绘图效果相当绚丽,而且还支持一些图片的操作功能,例如图片的缩放.可以看一下Flot ...

  6. 【POJ】3261 Milk Patterns

    http://poj.org/problem?id=3261 题意:一个长度为n的串,要求最长的子串的长度且这个子串的出现次数不少于k次.(1<=n<=20000, 2<=k< ...

  7. 【TYVJ】1307 联络员(最小生成树)

    http://tyvj.cn/Problem_Show.aspx?id=1307 kruskal裸题.(水题红色警报) #include <cstdio> #include <cst ...

  8. JavaScript验证函数大全

    1. 长度限制 <script> function test() { if(document.a.b.value.length>50) { alert("不能超过50个字符 ...

  9. css样式表:样式分类,选择器。样式属性,格式与布局

    样式表分类: 1.内联样式表, 和html联合显示,例:<p style="font-size:14px;">内联样式表</p> 2.内嵌样式表 作为一个独 ...

  10. 记linux终端下怎样退出>

    敲命令时候,敲错了,进入了>的提示下,怎么也退不出了.查了资料是按ctrl+c或者ctr+d都能退出