对C/C++内存分配的思考与总结

下图为操作系统内存地址空间图:

对C/C++内存分配的思考与总结

在C/C++中通常可以把内存划分为:
栈、堆、自由存储区、全局(静态)存储区、常量存储区

一个C/C++编译的程序占用的内存分为以下部分:
1. 栈(stack):存储 局部变量函数参数返回地址等。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。 栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

2.堆(heap):存放动态分配(malloc)的内存。 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS(Operating System)回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
//在C++中需区分堆和自由存储区(自由存储是C++中通过new与delete动态分配和释放对象的抽象概念,而堆(heap)是C语言和操作系统的术语,是操作系统维护的一块动态分配内存。new所申请的内存区域在C++中称为自由存储区。藉由堆实现的自由存储,可以说new所申请的内存区域在堆上。堆与自由存储区还是有区别的,他们并非等价)

3. 全局(静态static)区(.bss段/.data段):存放 全局变量静态变量。包含.bss段(存放未初始化或初值为0的全局变量和静态局部变量)和.data段(存放已初始化或初值非0的全局变量和静态局部变量),注意:在C++中不会去区分全局变量是否初始化,他们共同占用同一块内存区域。

4.文字常量区(.rodata段)常量字符串就放在这里的不允许修改,程序结束后由系统释放。

5. 代码存储区(.text段):存放 函数二进制代码
示例代码如下:
  C Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include < stdio.h>
#include < stdlib.h >

int 0//全局初始化区(.data段)
char *p1; //全局未初始化区(.bss段)

int main()
{
    
int b; //栈
    char s[] "abc"//栈
    char *p2; //栈

    
char *p3 "123456"//123456在常量区 p3在栈区。

    
static int 0//全局(静态)初始化区

    p1 (
char *)malloc(10);
    p2 (
char *)malloc(20);//malloc分配的10和20字节均在堆申请

    strcpy(p1, 
"123456");//123456放在文字常量区,编译器可能会将它与p3所指向的"123456"优化在一个内存块
    return 0;
}

--------------------------------------------------------------------------------------------
堆和栈的生长方向不同:
堆的生长方向是向着内存地址增加的方向, 栈的生长方向是向着内存地址减小的方向。当栈和堆生长到开始相互覆盖时,则称为 “栈堆冲突”,系统肯定垮台。

判断栈是由高地址向低地址生长
代码如下:
  C Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#includestdio.h >
static int stack_dir;
static void find_stack_direction (void)
{
    
static char   *addr NULL //address of first'dummy', once known
    
char     dummy;    //to get stack address

    
if (addr == NULL)
    {
        
//initial entry
        addr &dummy;
        printf(
"fir %p %p\n"addr, &dummy);
        find_stack_direction ();   
//recurse once
    }
    
else                     //second entry
    {
        printf(
"sec %p %p\n"addr, &dummy);
        
if (&dummy >= addr)
            stack_dir 
1   //stack grew upward
        
else
            stack_dir -
1;
                             
//stack grew downward
}

int main(void)
{
    find_stack_direction();
    
if(stack_dir == 1)
        puts(
"stack grew upward");
    
else
        puts(
"stack grew downward");
    
return 0
;
}


输出结果为:
fir   : 0028FEFF0028FEFF
sec : 0028FEFF 0028FECF
stack grew downward
---------------------------------------------------------------------------------------------
find_stack_direction函数使用函数递归的方法
第一次进入,由于addr为NULL,所以将字符变量dummy的地址赋值给静态变量addr
第二次进入,由于静态变量addr已赋了值,所以进入 "Second entry."
接着,将第二次进入的dummy地址和第一次进入的dummy地址相比较
如果值非负,则堆栈向高地址增长;否则,堆栈向低地址增长

------------------------------------------------------------------------------------------------
对内存的基本了解后,也引发了我的一些思考:
  C Code 
1
2
3
4
5
6
7
#include < stdio.h >
int main(void)
{
    
int *x;
    *x 
3;
    
return 0
;
}

这段代码在定义指针*x的时候 并没有明确它的指向(未初始化),该指针可能指向任意一块内存,之后第五行代码则是对该处内存区域的数据进行修改,这样的操作很可能访问到非法内存导致段错误,当然也可能因为没访问到非法内存而没有产生段错误,但是一个健壮的程序不允许存在这样的隐患。所以说对数据的初始化是不可缺少的习惯。

可能出现内存泄露的几种情况:
情况一:同时当我们在使用malloc多次申请内存的时候(申请堆区),一定要进行相应的 free来释放指针所指向的地址空间。如果多次malloc而只进行一次free的话,也会造成内存泄露。
情况二:malloc申请地址空间后,还未用free释放该空间时就将指向该空间的指针置为NULL。

接下来说说free这个函数,来看下面这段程序:
  C Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
#include < stdio.h>
#include < stdlib.h >

int main()
{
    
int  *p (int *)malloc(4);
    printf("%p %d\n"&p, *p);
    *p 
10;
    printf(
"%p %d\n"&p, *p);
    free(p);
    printf(
"%p %d\n"&p, *p);
    
return 0
;
}

运行结果:
0028FF1C 3544168
0028FF1C 10
0028FF1C 3544168

我们知道 free函数的用法是释放指针所指的内存空间(切记不是释放指针),然后根据运行结果我们可以推断出实际上虽然内存空间已被释放,但是该指针若不置为NULL( 切记free后将指针置为NULL),则它所指向的地址空间还是刚刚已经释放的地址空间(上述程序显示指针指向的那块内存空间地址未发生改变)。
我们可以看到原本该地址空间的值为3544168,当我们修改该地址空间的值让其变为10后再释放该空间,接下来如果我们访问这块空间,会发现这块地址空间居然还是3544168,而不是10。至于为什么,我们不去追究,不同的操作系统有不同的解决方式。
其实我想说的只有两点
(1).你可以不遵守规则,但不等于没有规则。 (2).不遵守规则而产生的结果是不可预测的。

https://segmentfault.com/q/1010000000253786(这里是对free释放内存后,对于内存空间疑问的讨论)

总结
我们应该从这些点滴看起,当我们遇到一个很大的项目时,如果我们能在这些地方做到细微的注意,那么我们将会写出健壮的代码。
----------------------------------------------------------------------------------------------
参考资料:
http://www.cnblogs.com/QG-whz/p/5060894.html(C++自由存储区不等价于堆的解释)
http://www.cnblogs.com/xkfz007/archive/2012/06/22/2558935.html(栈增长方向与大端小端问题)
http://crazyof.me/blog/archives/995.html(堆栈及内存动态分配的解释,非常详细,推荐阅读!!)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值