内存分配中的栈和堆
    内存中的栈区处于相对较高的地址以地址的增长方向为上的话,栈地址是向下增长的,栈中分配局部变量空间,堆区是向上增长的用于分配程序员申请的内存空间。另外还有静态区是分配静态变量,全局变量空间的;只读区是分配常量和程序代码空间的;以及其他一些分区。
    image.png
    .data 数据段 初始化
    .bss bss段 未初始化
    1、内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory。
    2、内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
    memory leak会最终会导致out of memory!
    内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。一个盘子用尽各种方法只能装4个果子,你装了5个,结果掉倒地上不能吃了。这就是溢出!比方说栈,栈满时再做进栈必定产生空间溢出,叫上溢,栈空时再做退栈也产生空间溢出,称为下溢。就是分配的内存不足以放下数据项序列,称为内存溢出.
    3、内存访问越界:向系统申请了一块内存,在使用这块内存的时候,超出了你申请的范围。
    4、缓冲区溢出:
    缓冲区溢出是指当计算机向缓冲区内填充数据位数时超过了缓冲区本身的容量溢出的数据覆盖在合法数据上,理想的情况是程序检查数据长度并不允许输入超过缓冲区长度的字符,但是绝大多数程序都会假设数据长度总是与所分配的储存空间相匹配,这就为缓冲区溢出埋下隐患。操作系统所使用的缓冲区 又被称为”堆栈”。在各个操作进程之间,指令会被临时储存在”堆栈”当中,”堆栈”也会出现缓冲区溢出。
    5、栈溢出:
    栈溢出就是缓冲区溢出的一种。 由于缓冲区溢出而使得有用的存储单元被改写,往往会引发不可预料的后果。程序在运行过程中,为了临时存取数据的需要,一般都要分配一些内存空间,通常称这些空间为缓冲区。如果向缓冲区中写入超过其本身长度的数据,以致于缓冲区无法容纳,就会造成缓冲区以外的存储单元被改写,这种现象就称为缓冲区溢出。
    栈溢出就是缓冲区溢出的一种。
    例子:数组越界访问或输入越界引起的死循环
    局部变量在栈中的分布方式:内存分配中,栈地址是向下增长的。低位数据存在低地址位

    1. int i,a[10];
    2. for (i=0; i<=10; i++) {
    3. a[i]=0;
    4. }

    这个循环看似正常,但会导致死循环。原因在于循环越界,访问了a[10]的位置,即赋值语句a[10]=0,而根据局部变量在栈中的分布方式,a[10]的位置就是变量i的位置,所以使i赋值为0,导致循环条件依然成立,陷入死循环。

    1. int i;
    2. char c;
    3. for(i=0;i<5;i++) {
    4. scanf(“%d”,&c);
    5. printf(“%d ”,i)
    6. }
    7. 输入:0 1 2 3 4
    8. 输出:0 0 0 0 0

    如果输入小于256,则这也是一个死循环,输出就一直是0。原因在于”scanf(“%d”,&c)”,c是一个char类型,但以%d整型的方式存储,即从c的存储位置开始存储4个字节的数据。i和c的存储位置如下:

    i+3 i+2 i+1 i c

    左边为高地址,右边为低地址。当输入一个值时,相当于把【i+2,…,c】当做一个int,低8为存储于c中,高24存在【i+2,…,i】中,即每次都把i的值部分覆盖,如果输入的值小于256,则被覆盖的数据永远是0,所以i恒等于0,进入死循环中。如果输入的值大于等于1280,循环结束。

    容易引发内存越界的操作:
    sprintf snprintf
    vsprintf vsnprintf
    strcpy strncpy strcat
    memcpy memmove memset bcopy