从一个微型例子看“C/C++的内存分配机制”和“数组变量名与指针变量名”

 

内存分配方式有三种: 

  1. 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
  2. 在栈上创建。 在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 这个大小在linux上可以使用:ulimit -a | grep stack 来查看
  3. 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。 
#include  < iostream >
using   namespace  std;
char   *  GetMemory( void ){
    
// p_arr是个数组的起始地址,这个数组的内容是存放在栈上面的。另外,非常值得一
    
// 提的是:系统没有为p_arr单独分配一个指针变量用来存放数组p_arr的起始地址。 
     char  p_arr[]  =   " p_arr : hello world " ;

    
// p_heap是一个指针变量,这个指针变量存在于栈上面,指针变量的值是指向的由
    
// malloc分配得到的堆上的内存空间的起始地址。
     char   * p_heap = ( char   * )malloc( sizeof ( char ) * 20 );

    
// ptr是一个指针变量,这个指针变量存在于栈上面,指针变量的值是指向的后面字符
    
// 串的起始地址,这个字符串是常量,是存在于静态存储区域的,不是在栈上,也不是
    
// 在堆上。
     char   * ptr  =   " ptr : hello world " ;

    strcpy(p_heap,
" p_heap: hello world " );
    printf(
" %08X\n " ,p_arr);
    printf(
" %08X\n " , & p_arr);
    printf(
" %08X\n " ,p_heap);
    printf(
" %08X\n " , & p_heap);
    printf(
" %08X\n " ,ptr);
    printf(
" %08X\n " , & ptr);
    puts(p_arr);
    puts(ptr);
    puts(p_heap);
    
// return p_arr;
     return  ptr;
}
int  main(){
    
char   *  str = NULL;
    str 
=  GetMemory();
    printf(
" %08X\n " ,str);
    printf(
" %08X\n " , & str);
    puts(str);
    
return   0 ;
}
上面程序输出的结果是:
DC726BF0 
DC726BF0
0EB29010
DC726BE8
004009D8
DC726BE0
p_arr : hello world
ptr : hello world
p_heap: hello world
004009D8
DC726C28
ptr : hello world
 

这里面8有8行关于地址的输出,分别描述如下:

  • DC726BF0这个地址是栈上面的,函数退出后,这块地址空间就废弃了。
  • DC726BF0是栈上面的地址,之所以和上面一样,是因为栈上并没有分配一个单元用来存放数组指针,这个是使用char []和char * 的最根本的区别。
  • 0EB29010这个地址是在堆上面的,直到delete才会释放掉这块内存。
  • DC726BE8是栈上面的地址,用来存放指针变量p_heap的单元,p_heap的值就是上面堆上内存的地址。
  • 004009D8这个地址是静态存储区的,函数退出后不会清除掉 
  • DC726BE0栈上面的地址,用来存放指针变量ptr的单元,ptr的值就是上面静态的存储区地址
  • 004009D8这个是由函数返回的地址,就是上面静态存储区的地址
  • DC726C28这个地址是main函数局部变量的存放地址,位置要比栈上的位置低 
从这个例子上也可以看出, 栈的地址空间、堆的地址空间、静态存储空间、main函数的局部地址空间的关系,地址位置从高到低关系如下:

栈-->main函数局部变量地址-->堆-->静态变量地址

可以看出来,静态地址最低,然后依次是:堆、main、栈。

 

 

另外的,还有一个“先声明的变量时在高地址区还是在低地址区”的问题,这个问题要看栈怎么生长的了,如果栈底在高内存,向低地址生长,自然先声明的变量在高地址。例如:在linux中,栈底就是在高地址的,所以后面声明的变量地址就是低地址。此外,在堆上的地址一般是从低到高的。下面的例子可以清晰的看出这两种情况。  

#include  < iostream >
class  c1
{
    
public :
        
int  number;
};
main()
{
    
int  a  = 3 ;
    
int  b  = 4 ;
    printf(
" addr of a on stack: 0x%X\n " , & a);
    printf(
" addr of b on stack: 0x%X\n " , & b);
    c1 
*  c = new  c1();
    c1 
*  d = new  c1();
    
int   *  e  = new   int ( 100 );
    printf(
" addr of c on stack: 0x%X--0x%X(on heap)\n " , & c,c);
    printf(
" addr of d on stack: 0x%X--0x%X(on heap)\n " , & d,d);
} 

 

以上代码的输出:
addr of a on stack: 0x26F8A744
addr of b on stack: 0x26F8A740
addr of c on stack: 0x26F8A738--0xE7F1010(on heap)
addr of d on stack: 0x26F8A730--0xE7F1030(on heap)

 

对于上面的输出解释如下:

  • a,b是位于main函数栈上的变量,他们地址的增长方向是从高到低的。另外,也请注意a和b的地址相差的距离是4个字节(虽然是64位机器和OS,但是int占用的空间还是32位)
  • c,d是位于main函数栈上的指针变量,同样是从高到低增长。这次要注意,他们相差的距离是8个字节(因为是c和d都是指针变量,要能够存下64位地址长度的数据)
  • 最后,要注意c和d指向的结构都位于堆上,这次他们地址的增长方向就是从低到高(从0xE7F1010到0xE7F1030)了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值