Mr Dk.'s BlogMr Dk.'s Blog
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • 📝 Notes
    • Algorithm
      • Algorithm - Bloom Filter
      • Algorithm - Disjoint Set
      • Algorithm - Fast Power
      • Algorithm - KMP
      • Algorithm - Monotonic Stack
      • Algorithm - RB-Tree
      • Algorithm - Regular Expression
      • Algorithm - Sliding Window
      • Online Judge - I/O
    • C++
      • C++ - Const
      • C++ File I/O
      • C++ - Object Layout
      • C++ - Operator Overload
      • C++ - Polymorphism
      • C++ STL algorithm
      • C++ STL map
      • C++ STL multimap
      • C++ STL priority_queue
      • C++ STL set
      • C++ STL string
      • C++ STL unordered_map
      • C++ STL vector
      • C++ - Smart Pointer
      • C++ - Template & Genericity
    • Compiler
      • ANTLR - Basic
      • Compiler - LLVM Architecture
      • Compiler - Multi-version GCC
    • Cryptography
      • Cryptography - Certbot
      • Cryptography - Digital Signature & PKCS #7
      • Cryptography - GPG
      • Cryptography - JWT
      • Cryptography - Keystore & Certificates
      • Cryptography - OAuth 2.0
      • Cryptography - Java 实现对称与非对称加密算法
      • Cryptography - TLS
    • DevOps
      • DevOps - Travis CI
    • Docker
      • Docker - Image & Storage Management
      • Docker - Image
      • Docker - Libcontainer
      • Docker - Multi-Arch Image
      • Docker - Multi-Stage Build
      • Docker - Network
      • Docker - Orchestration & Deployment
      • Docker - Overview
      • Docker - Service Building
      • Docker - Volume & Network Usage
      • Docker - Volume
      • Linux - Control Group
      • Linux - Namespace
    • Git
      • Git - Branch & Merge
      • Git - Cached
      • Git - Cherry Pick
      • Git - Commit
      • Git - Patch
      • Git - Proxy
      • Git - Rebase
      • Git - Reset
      • Git - Stash
      • Git - Theme for Git-Bash
    • Java
      • JVM - Synchronized
      • JVM - Volatile
      • Java - Annotation 注解
      • Java - BIO & NIO
      • Java - Class Path
      • Java - Condition and LockSupport
      • Java - Current Timestamp
      • Java - Deep Copy
      • Java - 运行环境配置
      • Java - Equals
      • Java - Exporting JAR
      • Java - Javadoc
      • Java - Lock
      • Java - Maven 项目构建工具
      • Java - References
      • Java - Reflection Mechanism
      • Java - String Split
      • Java - Thread Pool
      • Java - Thread
      • Tomcat - Class Loader
      • Tomcat - Container
    • Linux
      • addr2line
      • cut
      • df
      • du
      • fallocate
      • find
      • fio
      • grep
      • groupadd
      • gzip
      • head / tail
      • hexdump
      • iostat
      • iotop
      • kill
      • ldd
      • lsof
      • ltrace / strace
      • mpstat
      • netstat
      • nm
      • pidstat
      • pmap
      • readlink
      • readlink
      • rpm2cpio / rpm2archive
      • sort
      • tee
      • uniq
      • useradd
      • usermod
      • watch
      • wc
      • which
      • xargs
    • MS Office
      • MS Office - Add-in Dev
      • MS Office - Application
    • MySQL
      • InnoDB - Architecture
      • InnoDB - Backup
      • InnoDB - Checkpoint
      • InnoDB - Critical Features
      • InnoDB - Files
      • InnoDB - Index
      • InnoDB - Insert Buffer
      • InnoDB - Lock
      • InnoDB - Partition Table
      • InnoDB - Table Storage
      • MySQL - Server Configuration
      • MySQL - Storage Engine
    • Network
      • Network - ARP
      • Network - FTP
      • Network - GitHub Accelerating
      • HTTP - Message Format
      • HTTP - POST 提交表单的两种方式
      • Network - Proxy Server
      • Network - SCP
      • Network - SSH
      • Network - TCP Congestion Control
      • Network - TCP Connection Management
      • Network - TCP Flow Control
      • Network - TCP Retransmission
      • Network - Traceroute
      • Network - V2Ray
      • Network - WebSocket
      • Network - Windows 10 Mail APP
      • Network - frp
    • Operating System
      • Linux - Kernel Compilation
      • Linux - Multi-OS
      • Linux - Mutex & Condition
      • Linux - Operations
      • Linux: Package Manager
      • Linux - Process Manipulation
      • Linux - User ID
      • Linux - Execve
      • OS - Compile and Link
      • OS - Dynamic Linking
      • OS - ELF
      • Linux - Image
      • OS - Loading
      • OS - Shared Library Organization
      • OS - Static Linking
      • Syzkaller - Architecture
      • Syzkaller - Description Syntax
      • Syzkaller - Usage
      • Ubuntu - Desktop Recover (Python)
      • WSL: CentOS 8
    • Performance
      • Linux Performance - Perf Event
      • Linux Performance - Perf Record
      • Linux Performance - Perf Report
      • Linux Performance - Flame Graphs
      • Linux Performance - Off CPU Analyze
    • PostgreSQL
      • PostgreSQL - ANALYZE
      • PostgreSQL - Atomics
      • PostgreSQL - CREATE INDEX CONCURRENTLY
      • PostgreSQL - COPY FROM
      • PostgreSQL - COPY TO
      • PostgreSQL - Executor: Append
      • PostgreSQL - Executor: Group
      • PostgreSQL - Executor: Limit
      • PostgreSQL - Executor: Material
      • PostgreSQL - Executor: Nest Loop Join
      • PostgreSQL - Executor: Result
      • PostgreSQL - Executor: Sequential Scan
      • PostgreSQL - Executor: Sort
      • PostgreSQL - Executor: Unique
      • PostgreSQL - FDW Asynchronous Execution
      • PostgreSQL - GUC
      • PostgreSQL - Locking
      • PostgreSQL - LWLock
      • PostgreSQL - Multi Insert
      • PostgreSQL - Plan Hint GUC
      • PostgreSQL - Process Activity
      • PostgreSQL - Query Execution
      • PostgreSQL - Spinlock
      • PostgreSQL - Storage Management
      • PostgreSQL - VFD
      • PostgreSQL - WAL Insert
      • PostgreSQL - WAL Prefetch
    • Productivity
      • LaTeX
      • Venn Diagram
      • VuePress
    • Solidity
      • Solidity - ABI Specification
      • Solidity - Contracts
      • Solidity - Expressions and Control Structures
      • Solidity - Layout and Structure
      • Solidity - Remix IDE
      • Solidity - Slither
      • Solidity - Types
      • Solidity - Units and Globally Available Variables
    • Vue.js
      • Vue.js - Environment Variable
    • Web
      • Web - CORS
      • Web - OpenAPI Specification
    • Wireless
      • Wireless - WEP Cracking by Aircrack-ng
      • Wireless - WPS Cracking by Reaver
      • Wireless - wifiphisher

C++ - Object Layout

Created by : Mr Dk.

2021 / 03 / 06 16:17

Nanjing, Jiangsu, China


About

一个 C++ 对象的内存布局是什么样的?

程序在运行时会有几个内存区:

  • 数据段
  • BSS 段
  • 代码段
  • 栈
  • 堆

一个 C++ 对象的各部分分别在哪个位置上呢?

Original

首先明确一点,在 C 语言中:

  • 全局变量和静态变量存储在数据段 (已被初始化) 和 BSS 段 (未被初始化) 中
  • 代码 (函数) 全部保存在代码段中
  • 函数内的局部变量位于栈上 (在编译时已经可以确定空间大小)
  • 在代码中动态分配的内存位于堆上 (不确定空间大小,动态分配)

C++ Object

根据上述思路,以一个位于函数内的局部变量对象为例。在对象被声明后,肯定会在栈内占用内存空间。栈内占用内存的大小在编译时已经可以确定,包含:

  • 非静态成员变量
  • (如果有虚函数) 虚函数表指针

其它部分去哪了呢?以下内容参考自 Vishal Chovatiya 的博客:

  • Memory Layout of C++ Object in Different Scenarios
  • Inside the C++ Object Model
class X {
    int         x;
    float       xx;
    static int  count;
public:
    X() {}
    virtual ~X() {}
    virtual void printAll() {}
    void printInt() {}
    void printFloat() {}
    static void printCount() {}
};

对象布局:

      |                        |
      |------------------------| <------ X class object memory layout
      |        int X::x        |
stack |------------------------|
  |   |       float X::xx      |
  |   |------------------------|      |-------|--------------------------|
  |   |         X::_vptr       |------|       |       type_info X        |
 \|/  |------------------------|              |--------------------------|
      |           o            |              |    address of X::~X()    |
      |           o            |              |--------------------------|
      |           o            |              | address of X::printAll() |
      |                        |              |--------------------------|
      |                        |
------|------------------------|------------
      |  static int X::count   |      /|\
      |------------------------|       |
      |           o            |  data segment
      |           o            |       |
      |                        |      \|/
------|------------------------|------------
      |        X::X()          |
      |------------------------|       |
      |        X::~X()         |       |
      |------------------------|       |
      |      X::printAll()     |      \|/
      |------------------------|  text segment
      |      X::printInt()     |
      |------------------------|
      |     X::printFloat()    |
      |------------------------|
      | static X::printCount() |
      |------------------------|
      |                        |

Member Function

成员函数去哪了?C++ 把所有的成员函数转换成了普通函数。编译器为函数加上类名作用域解析,以表示该函数属于哪个类;并为每个类成员函数 隐式传入一个参数:this 指针,指向调用成员函数的对象地址,这样成员函数内就可以访问成员变量了。以下面的类为例:

class foo {
    int m_var;
public:
    void print() {
        cout << m_var << endl;
    }
};

编译器将会对成员变量和成员函数分别对待。把成员变量留在类内,占用对象内存;把成员函数加上作用域解析,隐式传入 this 指针后,放进代码段中:

class foo {
    int m_var;
};

void foo::print(foo *this) {
    std::cout.operator<<(this->m_var).operator<<(std::endl);
}

Static Member Function

对于类内的静态成员函数,编译器同样对其加上作用域解析后,放进了代码段中。区别在于 不会将 this 指针作为隐式参数,因为这个函数不会被某个对象调用。

Static Member Variable

由于静态成员变量也不可能在每个对象实例中都有一个副本,因此不会与非静态成员变量一样放在栈上。被加上作用域解析后,被放进了数据段中。

Virtual Function

编译器自动为每个类内的所有虚函数生成一个 虚函数表,通常会放置在数据段中 (但具体取决于编译器的具体实现)。虚函数表中放置了指向代码段中相应函数入口的指针。表内的第一个条目是一个指向 type_info 对象的指针,该对象内包含了与当前类相关的继承信息。

编译器自动为每个带有虚函数的对象添加了一个成员变量 _vptr,该指针指向类的虚函数表。

Object Layout with Inheritance

在发生继承后,派生类对象中将会包含一个基类对象。因此,派生类对象中的内存中也会包含基类对象的所有非静态成员变量。

class X {
    int x;
    string str;
public:
    X() {}
    virtual ~X() {}
    virtual void printAll() {}
};
class Y : public X {
    int y;
public:
    Y() {}
    ~Y() {}
    void printAll() {}
};

如上述代码所示,基类对象中包含一个整形变量 x 和字符串对象 str;在派生类对象中,还另外附带了一个整型变量 y。内存布局如下图所示:

      |                              |
      |------------------------------| <------ Y class object memory layout
      |          int X::x            |
stack |------------------------------|
  |   |              int string::len |
  |   |string X::str ----------------|
  |   |            char* string::str |
 \|/  |------------------------------|      |-------|--------------------------|
      |           X::_vptr           |------|       |       type_info Y        |
      |------------------------------|              |--------------------------|
      |          int Y::y            |              |    address of Y::~Y()    |
      |------------------------------|              |--------------------------|
      |               o              |              | address of Y::printAll() |
      |               o              |              |--------------------------|
      |               o              |
------|------------------------------|--------
      |           X::X()             |
      |------------------------------|       |
      |           X::~X()            |       |
      |------------------------------|       |
      |         X::printAll()        |      \|/
      |------------------------------|  text segment
      |           Y::Y()             |
      |------------------------------|
      |           Y::~Y()            |
      |------------------------------|
      |         Y::printAll()        |
      |------------------------------|
      |      string::string()        |
      |------------------------------|
      |      string::~string()       |
      |------------------------------|
      |      string::length()        |
      |------------------------------|
      |               o              |
      |               o              |
      |               o              |
      |                              |

派生类复制了一份基类的虚函数表。如果派生类中重写了虚函数,那么就将虚函数表中的相应条目替换为派生类中相应函数的地址。

Differences between Structure and Class

在 C++ 中对 struct 进行了扩展,struct 与 class 的使用无异,除了一点:

  • struct 默认所有成员为 public;同样,在继承方式上,默认为公有继承
  • class 默认所有成员为 private;同样,在继承方式上,默认为私有继承

但是具体使用 struct 和 class 一般遵循约定:

  • 如果只是为了将一些元素捆为一个整体,那么可以使用 struct
  • 如果是为了高层次的建模、抽象,或者提供一种接口,那么就使用 class

参考:Fluent C++ - The real difference between struct and class

Edit this page on GitHub
Prev
C++ File I/O
Next
C++ - Operator Overload