(最重点--------设计到底层原理,数据很多地方错了的原因)
栈:在函数内部声明的所有变量都将占用栈内存。 而且是分配的静态内存, 方法结束自动释放
堆:这是程序中未使用的内存,在程序运行时可用于动态分配内存。 动态内存分配, 需要手动释放!
https://blog.csdn.net/yushiyaogg/article/details/36899515
内存分配方式有三种:
(1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
(2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。(系统分配, 系统也会自动释放)
(3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
类的静态成员变量和函数中的静态变量一样,在编译的时候就分配内存了,直到程序退出才释放,并不是随着对象的删除而释放的:
36、为什么需要使用堆,使用堆空间的原因?
【参考答案】
直到运行时才知道一个对象需要多少内存空间;不知道对象的生存期到底有多长。
这个例子把4区模型画一下,c 内存的四驱模型是怎么样的
#include
usingnamespacestd;
char*getStr1(){
char*str="12345";
returnstr;
}
char*getStr2(){
char*str="12345";
returnstr;
}
char*getStr3(){
charbuff[128];
strcpy(buff,"12345");
returnbuff;
}
char*getStr4(){
char*buff=malloc(128);
strcpy(buff,"12345");
returnbuff;
}
voidmain()
{
char*str1=NULL;
char*str2=NULL;
char*str3=getStr3();
char*str4=getStr4();
}
st1和str2指向一样! 是同一块内存, 因为他们是常量取区
st3不是, 而且值也不是12345. 因为它在栈区,被释放了
str4, 堆区,不同的内存 ,但是值还是12345. 因为它一直在
画图的时候“12345”是在全局区, str1是在栈区.指向全局区
容易出错的题目:
76、以下三条输出语句分别输出什么?
char str1[] = "abc";
char str2[] = "abc";
const char str3[] = "abc";
const char str4[] = "abc";
const char* str5 = "abc";
const char* str6 = "abc";
cout << boolalpha << ( str1==str2 ) << endl; // 输出什么?
cout << boolalpha << ( str3==str4 ) << endl; // 输出什么?
cout << boolalpha << ( str5==str6 ) << endl; // 输出什么?
【参考答案】
分别输出 false,false,true。
str1和str2都是字符数组,每个都有其自己的存储区,它们的值则是各存储区首地址,不等;
str3和str4同上,只是按const语义,它们所指向的数据区不能修改。
str5和str6并非数组而是字符指针,并不分配存储区,其后的“abc”以常量形式存于静态数据区,而它们自己仅是指向该区首地址的指针,相等。
3. new 运算符的作用!
在 C++ 中,您可以使用特殊的运算符为给定类型的变量在运行时分配堆内的内存,这会返回所分配的空间地址。这种运算符即 new 运算符。
如果您不再需要动态分配的内存空间,可以使用 delete 运算符,删除之前由 new 运算符分配的内存。
new int 是什么意思?
如果自由存储区已被用完,可能无法成功分配内存。所以建议检查 new 运算符是否返回 NULL 指针,并采取以下适当的操作:
double*pvalue=NULL;
if(!(pvalue=newdouble))
{
cout<<"Error: out of memory."< exit(1); } 在C语言中,动态分配内存用 malloc() 函数,释放内存用 free() 函数 malloc() 函数在 C 语言中就出现了,在 C++ 中仍然存在,但建议尽量不要使用 malloc() 函数。 new 与 malloc() 函数相比,其主要的优点是,new 不只是分配了内存,它还创建了对象。 #include usingnamespacestd; intmain() { double*pvalue=NULL;// 初始化为 null 的指针 pvalue=newdouble;// 为变量请求内存 *pvalue=29494.99;// 在分配的地址存储值 cout<<"Value of pvalue : "<<*pvalue< deletepvalue;// 释放内存 return0; } 对象数组 SpeechEngine[] speechEngines = new SpeechEngine[4]; #include usingnamespacestd; classBox { public: Box() { cout<<"调用构造函数!"< } ~Box() { cout<<"调用析构函数!"< } }; intmain( ) { Box*myBoxArray=newBox[4]; delete[]myBoxArray;// 删除数组 return0; } 对象指针数组! 为了避免内存泄露,通常 new 和 delete 在 c/c++ 中我们将运行时数据,分为四个区域分别是:栈区,堆区,数据区,代码区。我们详细来介绍下: 栈区:由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。 堆区:一般由程序员分配释放, 若程序员不释放,程序结束时可能由 OS 回收。 数据区:存放全局变量、静态变量和常量字符串等等。 程序结束后由系统释放。 存放函数体的二进制代码。 栈区: voidchange2(int*a){*a=200;}voidchange1(inta){a=200;}voidmain(){inta=100;intb=10;change1(b);change2(&a);printf("change1 =%d,change2=%d",b,a);getchar();}//结果:change1 =10,change2=200 传递值: 从结果上发现changge1()这个方法并没有改变b的值,而change2 () 改变了 当执行到change1(b)函数体内的时候,只是把变量a的值给了change1()函数的a.当该函数体执行完后,因为栈区时会自己回收的所以change1()的内存数据是会被清空的。所以b的值没有改变 b=10。 int类型不能传递值的原因:就是内存 传递地址: 当执行到change2(&a);函数体内的时候,因为是传递的是a地址,然后通过地址修改a 的值,当 change2(&a)执行完 内存被清空后,a 的值已经发生改变了。 总结: 栈说的清空是什么意思? 方法执行完就清空 常量区: char*getStr1(){char*str="12345";returnstr;}char*getStr2(){char*str="12345";returnstr;}voidmain(){char*str1=getStr1();char*str2=getStr2();printf("%d , %d",str1,str2);getchar();} 上面 str1 和 str2 的地址值是一样 .因为在常量池中! char*getStr(){charbuff[128];strcpy(buff,"12345");returnbuff;}voidmain(){char*str=getStr();printf("%s",str);getchar();} 不是12345, 因为栈区的值被释放了,但是地址引用还在! 堆区: char*getMem(){char*p1=NULL;p1=(char*)malloc(sizeof(char)*10);//申请内存空间returnp1;}voidmain(){char*tmp=NULL;tmp=getMem();strcpy(tmp,"aaabbb");//向tmp做指向的内存空间中copy数据printf("tmp:%s.\n",tmp);getchar();} 4.栈的开口方向 debug模式下:向下 release模式下:向上 void main(){ int a = 10; int b = 10; // 0508 , 0496 a 的地址要大于 b 的地址 (debug) // 016 , 020 a 的地址要小于 b 的地址 (release) // buffer 数据类型跟栈的开口方向无关 (内部存储都是向上) char buff[10]; // buff+1 -> buff printf("%p , %p , %p , %p",&a,&b,buff,buff+1); int a1 = 100; int* ff= &a1; // 虽说是 a1 的地址,但是我也可以把 ff 看做一个数组 // char** 二位数组 char** 二级指针 ff += 1; getchar(); } https://www.cnblogs.com/Burning-youth/archive/2021/10/31/15488808.html C语音里面的内存划分(四驱模型) 栈区(栈内存,存放局部变量,自动分配和释放,里面函数的参数,方法里面的临时变量) 堆区(动态内存分配,C语音里面由程序员手动分配),最大值为操作系统的80% 全局区或静态区 常量区(字符串) 程序代码区 该静态内存定义为40M,而Window里面每一个应用栈大概是2M,超出了范围, 会报stack overflow错误 。 总结: 堆需要手动释放,栈区会自动释放!我们需要手动释放动态内存;动态内存是在堆分配的; 内存和野指针: 1. 哪些情况会内存泄露? 内存泄漏对于new和malloc都能检测出来,new可以明确指出是哪个文件的哪一行,但是malloc不可以明确指出是哪个文件的哪一行。 https://www.jianshu.com/p/a858eff90e94 内存泄露和野指针的监测 如何察觉到内存泄漏 泄露工具运行在哪个平台? window和linux! 如果程序在正常地使用过程中,占用的内存随着时间推移不断增长,一般就说明存在内存泄漏的情况。也可以使用专门的工具来检测程序中的内存泄漏: 'VLD感觉最好用 在vc++中可以使用 VLD(Visual LeakDetector) 进行检测,VLD 是一个免费开源的工具,只需要包含头文件即可,并且可以获取到内存泄漏的代码文件行号。 Tencent tMem Monitor是腾讯推出的一款运行时C/C++内存泄漏检测工具。TMM认为在进程退出时,堆内存中没有被释放且没有指针指向的无主内存块即为内存泄漏,并进而引入垃圾回收(GC, Garbage Collection)机制,在进程退出时检测出堆内存中所有没有被引用的内存单元,因而内存泄露检测准确率为100%。 gperftools 是 google 开源的一组套件,提供了高性能的、支持多线程的 malloc 实现,以及一组优秀的性能分析工具。gperftools 的 heap chacker 组件可以用于检测 C++ 程序中的内存泄露问题,它可以在不重启程序的情况下,进行内存泄露检查。 第三:静态代码检测工具 cppcheck 2. 动态申请内存 内存不够了,需要再加点内存,怎么办? 重新分配realloc 扩大内存 数据举例: 之前赋了值的, 可以在后面的基础上赋值就可以了! 释放的时候,除了新的数组要主动释放, 以前旧的数组要释放内存么? 3. 野指针 内存分配的几个注意细节 1.不能多次释放2.释放完之后(指针仍然有值),给指针置NULL,标志释放完成3.内存泄露(p重新赋值之后,再free,并没有真正释放内存)内存泄漏4, p重新赋值之前先free intmain() { if(androidBitmapInfo!=null){ free(androidBitmapInfo) } // 其他业务逻辑, // 不小心又重新调用了下 if(androidBitmapInfo!=null){ free(androidBitmapInfo) } return0; } 野指针: 它数据被释放了,但是内存还在! 需要把它执行另外一块内存,NUll 正确写法: 2. 野指针的监测: 静态分析工具: 在程序中,如果访问野指针,可能会造成程序的崩溃(也可能不会,但是没有造成程序崩溃会导致程序出现问题难以排查,还是崩溃了好一些) 使用智能指针(这个博主暂时也不会,先记一下) 如何避免“野指针”? 野指针”产生原因及解决办法如下: (1)指针变量声明时没有被初始化。解决办法:指针声明时初始化,可以是具体的地址值,也可让它指向 NULL。 (2)指针p被 free 或者 delete 之后,没有置为 NULL。解决办法:指针指向的内存空间被释放后指针应该指向 NULL。 (3)指针操作超越了变量的作用范围。解决办法:在变量的作用域结束前释放掉变量的地址空间并且让指针指向 NULL。 注意:“野指针”的解决方法也是编程规范的基本原则,平时使用指针时一定要避免产生“野指针”,在使用指针前一定要检验指针的合法性。 32、什么是野指针 野指针不是NULL指针,是未初始化或者未清零的指针,它指向的内存地址不是程序员所期望的,可能指向了受限的内存。 成因: 1)指针变量没有被初始化(容易忽略) 2)指针指向的内存被释放了,但是指针没有置NULL (常见) 3)指针超过了变量了的作用范围,比如b[10],指针b+11(数组) 10. 栈溢出是什么原因?