堆,栈等概念以及内存泄露,内存溢出,内存越界等问题 您所在的位置:网站首页 什么叫变量溢出问题的概念 堆,栈等概念以及内存泄露,内存溢出,内存越界等问题

堆,栈等概念以及内存泄露,内存溢出,内存越界等问题

2024-07-06 23:25| 来源: 网络整理| 查看: 265

内存4区:

在进程中内存被分为4个区域,分别是堆,栈,常量区,代码区。堆是我们程序员能够直接控制的内存区域(既是new malloc),栈是程序自己控制的区域,常量区是存放全局变量,字符串常量的区域以及存放代码的代码区。

在这之中堆栈是我们要注视的重点。下面我们通过一个程序了解堆栈的生长方向以及数据的生长方向。

int main(int argc, char* argv[]) { //堆 int y = 0; int arr[4] = { 0 }; int x = 0; printf("y = %d \n", y); printf("y = %p \n", &y); printf("x = %p \n", &x); for (int i = 0; i setting-->link,在category中选中output,然后再reserve中设定堆栈的最大值和 commit。

能否产生碎片:

对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题。

生长方向不同:

对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方式是向下的,是向着内存地址减小的方向增长。

分配方式不同:

堆都是动态分配的;栈有静态和动态两种分配方式。静态分配由编译器完成,比如局部变量的分配。动态分配由alloca函数进行、但栈的动态分配和堆是不同的,它的动态分配由编译器进行释放,无需我们手工实现。

分配效率不同:

栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是c/c++库函数提供的,机制很复杂。库函数会按照一定的算法进行分配。显然,堆的效率比栈要低得多。

注释:上面是我从网上抄下来的,一般了解就行了不用背,当然,如果要应付面试,觉得有必要的话就要背了,就像tcp与udp的特点  =  -  =。

堆栈的使用:

堆的使用就是  new  类型 ,delete 指针/delete[] 指针。malloc /calloc(区别是一个不会初始化为0,一会)。(new和malloc的区别是nwe可以调用类的构造函数完成初始化),关于new的用法可以去看 efftive c++。

栈我们是不能直接使用的,任何对栈的调用修改都会出现bug。虽然我们不可以手动使用它,但是我们可以简单的了解他的调用模型。

函数调用

函数调用分为以下几步:

参数入栈: 将参数按照调用约定(C 是从右向左)依次压入系统栈中;返回地址入栈: 将当前代码区调用指令的下一条指令地址压入栈中,供函数返回时继续执行;代码跳转: 处理器将代码区跳转到被调用函数的入口处;栈帧调整: 1.将调用者的ebp压栈处理,保存指向栈底的ebp的地址(方便函数返回之后的现场恢复),此时esp指向新的栈顶位置; push ebp 2.将当前栈帧切换到新栈帧(将eps值装入ebp,更新栈帧底部), 这时ebp指向栈顶,而此时栈顶就是old ebp mov ebp, esp 3.给新栈帧分配空间 sub esp, XXX

函数返回

函数返回分为以下几步:

保存被调用函数的返回值到 eax 寄存器中 mov eax, xxx恢复 esp 同时回收局部变量空间 mov ebp, esp将上一个栈帧底部位置恢复到 ebp pop ebp弹出当前栈顶元素,从栈中取到返回地址,并跳转到该位置 ret 内存问题:

c++绝大部分的bug都是内存使用错误,也就是对数组下标,指针等的使用错误。

主要有内存溢出,数组下标越界(内存越级),内存泄漏等。

细心的人应该发现了,我们在文章开头所写的代码中,int  y 被由0被改写成了5。这是因为y是先进栈,然后arr紧接着它进去了,所以他们的地址是相邻的,在我们for循环时,数组下标越界,给本不应该存在的arr[4]赋值为5,因为他们都是int类型,所以,y的地址就是arr[5]的地址,当给arr[5]赋值时就是给y赋值,所以y的值被改变为了5。这给一个没有分配空间的内存赋值的操作是违法的,一般称为内存写越界,相对的还有读越界,这2个都会破坏栈,造成不可预知的行为,比如上诉的y内改写为5.。当读写的是个非法地址(比如NULL)时就会造成程序崩溃。现在的编译器已经很智能了,内存越界/数组下标越界都会崩溃报错提示。上述的代码在vs2019就不可以运行,在vc++就可以运行。

内存泄漏就是我们new的内存delete没有释放。你说你不会忘记?呵,当代码很简单时,我们通常都不会忘记,但是百万航的代码呢?尽量减少这种错误的方法可以在类内的构造函数nwe,析构函数delete。尽量不要在函数new,在函数外delete,很容易忘记的。还有就是可以使用should_ptr这些自能指针,使用计数自动的帮你释放等。还有要记得new [](数组)要delete[] 指针 。具体的new优化可以看effective c++的new部分。

内存溢出就是分配的内存超出了栈的内存,不如上面的泄漏,积少成多就会造成溢出。

 

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有