网站权限控制,提供商城网站制作,win10优化大师好用吗,东营网站建设天锐科技温故而知新#xff0c;本文浅聊和回顾下C内存布局的知识。
一、c内存布局
C的内存布局主要包括以下几个部分#xff1a;
代码段#xff1a;存储程序的机器代码。.数据段#xff1a;存储全局变量和静态变量。数据段又分为初始化数据段#xff08;存储初始化的全局变量和…温故而知新本文浅聊和回顾下C内存布局的知识。
一、c内存布局
C的内存布局主要包括以下几个部分
代码段存储程序的机器代码。.数据段存储全局变量和静态变量。数据段又分为初始化数据段存储初始化的全局变量和静态变量和未初始化数据段存储未初始化的全局变量和静态变量。堆用于动态内存分配。当你使用new或malloc函数时内存会从堆中分配。栈用于存储局部变量和函数调用的信息例如返回地址和参数。当你调用一个函数时一个新的栈帧会被压入栈当函数返回时这个栈帧会被弹出。常量段存储常量字符串和其他常量。
代码示例
#include iostreamint global_var 0; // 初始化的全局变量存储在初始化数据段
int uninit_global_var; // 未初始化的全局变量存储在未初始化数据段void foo() {int local_var 0; // 局部变量存储在栈static int static_local_var 0; // 静态局部变量存储在初始化数据段int* dynamic_var new int(0); // 动态分配的内存地址在堆dynamic_var指针变量的生命周期是foo函数栈std::cout local_var: local_var std::endl;std::cout static_local_var: static_local_var std::endl;std::cout dynamic_var: dynamic_var std::endl;delete dynamic_var; // 释放动态分配的内存
}int main() {std::cout global_var: global_var std::endl;std::cout uninit_global_var: uninit_global_var std::endl;foo();return 0;
}二、C 类的内存布局
C类的内存布局主要取决于类的数据成员和继承关系。以下是一些基本的规则
数据成员变量类的数据成员按照它们在类定义中的顺序存储在内存中。每个数据成员的偏移量是它的类型对齐要求的倍数。
// x64
#pragma pack(push,4) //指定4字节对齐
class TmpClass{}; // 空类sizeof大小为1class NoVirtual
{
public:int m_i; // 4字节double m_d; // 8字节shared_ptrint m_ptr; // 8字节 》64bit system 4字节 》 32bit system
};
#pragma pack(pop) 成员函数在C中成员函数并不直接存储在每个对象中。相反所有对象共享同一个成员函数的副本。成员函数的代码存储在代码段而不是每个对象的内存空间。因此成员函数不影响类的sizeof大小。 当你调用一个对象的成员函数时编译器会自动将对象的地址作为隐藏参数传递给成员函数。这个隐藏参数通常被称为this指针。通过this指针成员函数可以访问调用它的对象的数据成员。
class NoVirtual
{void dc(){} // 成员函数内存在代码段
public:int m_i; // 4字节double m_d; // 8字节shared_ptrint m_ptr; // 8字节 》64bit system 4字节 》 32bit system
};静态成员变量静态成员变量不属于类的任何一个对象它们在所有对象之间共享。静态成员变量存储在全局数据段而不是对象的内存空间。 静态成员函数静态成员函数也不属于类的任何一个对象。它们没有this指针因此不能访问类的非静态成员。静态成员函数的地址存储在代码段。 继承如果一个类继承自一个或多个基类那么基类的数据成员会先于派生类的数据成员存储在内存中。如果有多个基类那么基类的数据成员按照它们在类定义中的顺序存储。
class Iface
{
public:Iface(){MYTRACE();}virtual ~Iface(){MYTRACE();}virtual void Ifun() 0;
};// 继承
class MemLayout : public Iface
{
public:MemLayout(){ MYTRACE(); }~MemLayout(){ MYTRACE(); }virtual void Ifun() override { MYTRACE(); }virtual void dc0(){ MYTRACE(); }virtual void dc1(){ MYTRACE(); }private:int m_num 0;static std::string m_desc;
};std::string MemLayout::m_desc hello; 虚函数如果一个类有虚函数(virtual关键字修饰)那么编译器会为这个类生成一个虚函数表vtable: 函数指针数组并在每个对象中添加一个指向虚函数表的指针(vptr)。虚函数表中存储了虚函数的地址。如果一个类继承自一个有虚函数的基类那么它会继承基类的虚函数表。
class VirtualClass
{virtual void dc(){}
public:int m_i; // 4字节double m_d; // 8字节shared_ptrint m_ptr; // 8字节 》64bit system 4字节 》 32bit system
};虚继承如果一个类使用虚继承那么编译器会为这个类生成一个虚基类表vbtable并在每个对象中添加一个指向虚基类表的指针。虚基类表中存储了虚基类的偏移量。直白点说虚继承的派生类的实例化对象可是唯一指向了两张虚函数表的下面有图为证~
class NoVirtual
{void dc(){}
public:int m_i; // 4字节double m_d; // 8字节shared_ptrint m_ptr; // 8字节 》64bit system 4字节 》 32bit system
};class Iface
{
public:Iface(){MYTRACE();}virtual ~Iface(){MYTRACE();}virtual void Ifun() 0;
};// 虚继承
class MemLayout : virtual public Iface
{
public:MemLayout(){ MYTRACE(); }~MemLayout(){ MYTRACE(); }virtual void Ifun() override { MYTRACE(); }virtual void dc0(){ MYTRACE(); }virtual void dc1(){ MYTRACE(); }private:int m_num 0;static std::string m_desc;
};std::string MemLayout::m_desc hello;