做了这么年的linux c开发,经常碰到各种内存问题。这里结合网上资料做一下总结。

一. 内存位置

 在C语言中,定义了4个内存区间:代码区;全局变量和静态变量区;局部变量区即栈区;动态存储区,即堆区;

 

1. bss段 block started by symbol. 存储没有被初始化的全局和静态变量。 

2. data段 存储被初始化的全局和静态变量

3. rodata段 常量变量。 read only. 用cons来修饰

4. text段 代码段

5. stack栈 用来储存局部变量,函数的参数值,地址向下增长。

6. heap堆 被programmer控制,由malloc和free获取或释放。

 

二. 各种内存错误 

1. 内存泄漏memory leak  调用malloc后没有free,导致内存剩余容量越来越少。解决方法:

a. 通过少量的实践和适当的文本搜索,您能够快速验证平衡的 malloc() 和 free() 或者 new 和 delete 的源主体

b. 运用实时监测工具, 如valgrind

 

2.  缓冲区溢出buffer overlow. 在分配的内存外的读写数据 

   stack overflow: 实际上是一种buffer overflow. 以下是维基百科的描述:

栈有自己的内存大小限制,栈大小由编程语言,机器架构,多线程等在程序开始决定的,当程序尝试使用超过栈空间的部分,就会出现overflow,程序崩溃。

stack overflow常用的导致方式是递归,重复调用自己,导致内存用尽

An example of infinite recursion in C

int foo() 
{
     return foo();
}

如果用了-O2/-O3,那么可能会有一些内部的优化使得程序不会有段错误)。一个递归例子:

 

 

int pow(int base, int exp) {

    if (exp > 0)

        return base * pow(base, exp - 1);

    else

        return 1;

}

int pow(int base, int exp) {

    return pow_accum(base, exp, 1);

}

 

int pow_accum(int base, int exp, int accum) {

    if (exp > 0)

        return pow_accum(base, exp - 1, accum * base);

    else

        return accum;

}

运行完结果对比:

  
pow(5, 4)
5 * pow(5, 3)
5 * (5 * pow(5, 2))
5 * (5 * (5 * pow(5, 1)))
5 * (5 * (5 * (5 * pow(5, 0))))
5 * (5 * (5 * (5 * 1)))
625
pow(5, 4)
pow_accum(5, 4, 1)
pow_accum(5, 3, 5)
pow_accum(5, 2, 25)
pow_accum(5, 1, 125)
pow_accum(5, 0, 625)
625

左边的代码要保存很多中间变量,一旦exp太大,会发生overflow。右边只需要保存三个变量,不会产生overflow。这样迭代风格代码的转换,只需要把上一次的迭代结果放在下一次的迭代的input里面就可以解决。很有技巧!

另外一个,是因为函数里面的局部变量太大,如:

int foo() 
{
     double x[1048576];
}

一个线程时,工作顺利。但是当多个线程时,程序崩溃, 这是因为多线程时stack空间比单线程小。

 

三. 内存碎片

分配的内存在内存空间内小而不连续。内存分配较小,并且分配的这些小的内存生存周期又较长,反复申请后将产生内存碎片的出现。如何避免:

a. 尽量少使用动态分配内存函数如malloc等。同时分配和释放要在同一函数里面。

b. 一次性分配一个很大的内存,自己做内存池,自己管理内存。

 

四. segmentation error 段错误。

段错误是指访问的内存超出了系统给的这个程序的内存空间。简单例子:

#include <stdio.h>
typedef struct TEST {
int a;
int b;
} test;
int main(){
 test *t = NULL;
 t->a = 12;
 return 0;
}

 运行完程序报segmentation error. 可以用gdb去debug. 如下图, 在compile的时候,加上-g. 运行时, gdb xxx(运行的程序)进入debug界面,run完之后,在backtrace,查出错的路径。

小窍门: 在-O3优化中发生segmentation error, 复杂代码可能用gdb backtrace 找不到正确的位置,那么我们可以在大概的位置加上

#pragma opt_level = "O0"

强制它变成O0。

 

posted on 2018-12-20 22:38  飞行的俊哥  阅读(264)  评论(0编辑  收藏  举报