【C++再学习】【06】程序以及对象在内存中的布局

首先介绍一下最基本的常识,C++程序占用的内存空间布局情况:

        1、栈区(stack):又编译器自动分配释放,存放函数的参数值,局部变量的值等,其操作方式类似于数据结构的栈。

        2、堆区(heap):一般是由程序员分配释放,若程序员不释放的话,程序结束时可能由OS回收,值得注意的是他与数据结构的堆是两回事,分配方式倒是类似于数据结构的链表。

        3、全局区(static):也叫静态数据内存空间,存储全局变量和静态变量,全局变量和静态变量的存储是放一块的,初始化的全局变量和静态变量放一块区域,没有初始化的在相邻的另一块区域,程序结束后由系统释放。

        4、文字常量区:常量字符串就是放在这里,程序结束后由系统释放。

        5、程序代码区:存放函数体的二进制代码。

 

        对于C++中的对象来说,举个例子比如在main函数中初始化的对象a,它包含一些数据成员以及函数成员,其中数据成员就存放在属于main函数的栈区上,因为函数代码是类的所有对象公用的,所以成员函数存放在上述程序代码区,只保存一份即可。下边来一个例子:

class A
{
public:
    int data1;
    long data2;
    char* str;       
        A();
    ~A(){delete [] str;}
    int getdata1()const{return data1;}
    long getdata2()const{return data2;}
    void setdata1(int d){data1 = d;}
    void setdata2(long d){data2 = d;}
    char * getstr()const{return str;}
 
};
A::A()
{
    data1 = data2 = 0;
    str = new char[50];
    strcpy(str,"hello");
}
int main() {
 
    A a,b;
    cout<<"a address:"<<(int*)&a<<endl;
    cout<<"a.data1 address:"<<(int*)&a.data1<<endl;
    cout<<"a.data2 address:"<<(int*)&a.data2<<endl;
    cout<<"a.str address:"<<(int*)a.str<<endl;
    cout<<endl;
    cout<<"b address:"<<(int*)&b<<endl;
    cout<<"b.data1 address:"<<(int*)&b.data1<<endl;
    cout<<"b.data2 address:"<<(int*)&b.data2<<endl;
    cout<<"b.str address:"<<(int*)b.str<<endl;
    cout<<endl;
    //cout<<&A::getdata1<<endl;
 
    printf("A::getdata1 addr=0x%lx\n", &A::getdata1); 
    printf("A::getdata2 addr=0x%lx\n", &A::getdata2);
    printf("A::setdata1 addr=0x%lx\n", &A::setdata1);
    printf("A::setdata2 addr=0x%lx\n", &A::setdata2);
    return 0;
}

        先说说怎么输出对象的数据成员以及函数成员地址,数据成员地址直接用(int*)&对象名 就可以了,而由于<<并没有对void(__thiscall A::*)()类型重载,编译器将这种类型转换为bool类型,所以无法通过 cout<<&A::getdata1输出成员函数的地址,而静态函数并非__thiscall<<有对它的重载,因此类的静态函数可以直接用cout输出函数地址。 我们可以用printf输出成员函数的地址,因为它可以接收任意类型的参数,包括__thiscall类型,如上边代码中可以看到。

                                                    

        这个是上述代码的输出结果,运行环境是win7+VC6.0,根据这个结果可以发现对象ab的数据成员data1data2他们的地址都是差不多相邻的,而abstr指针值有跟data1data2的地址相差很远,这是因为动态分配的原因,数据被分配到堆区域中,再看看类A的成员函数地址,跟对象ab的数据成员也不在一块,实际上是在程序代码区,A的所有的对象共用这些成员函数。看看下边这个图会更加清晰一些。不同的区域用不同的颜色分开,从内存分配布局来看看刚才那个程序。

               

        根据这个图我们可以很容易发现对于C++程序中跟对象相关的内存布局。下面有了一个问题,比如说a对象调用成员函数getdata1即:a.getdata1( );而成员函数内部会去取得数据成员data1,这个过程编译器是怎么完成的呢,我们知道类的任何一个成员函数都包含一个隐藏的this指针参数,这个指针指向了调用此成员函数的对象的地址,有了这个地址,根据上边a的地址就是数据成员的起始地址,加上一定的偏移量,成员函数就可以找到data1或者data2了,所以说再我们写程序a.getdata1( )时,实际上编译器已经把它换做了有this指针参数的全局函数A::getdata1( A* const register this),类的成员函数的本质就是一个C语言中的全局函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值