Unlocking C++ Object Memory Layout: From Basics to Inheritance and ASLR
This article walks through C++ object memory layout using concrete examples, covering basic data members, methods, private and static members, inheritance (with and without virtual functions), compiler optimizations, and address‑space layout randomization, all demonstrated with GDB inspections and code snippets.
1. Simple object memory layout
We start with a minimal
Basicclass containing an
intand a
double. After compiling and running, GDB shows the object's start address (
0x7fffffffe3b0) and the addresses of
aand
b(
0x7fffffffe3b0and
0x7fffffffe3b8), illustrating that
aoccupies the first four bytes and
bstarts eight bytes later due to alignment padding.
<code>#include <iostream>
using namespace std;
class Basic {
public:
int a;
double b;
};
int main() {
Basic temp;
temp.a = 10;
return 0;
}</code>Alignment ensures that each member starts at an address that matches the platform’s alignment requirements, improving memory‑access efficiency.
2. Object with methods
Adding a member function
setBdoes not change the object size; the method lives in the program’s text segment. GDB confirms the method address (
0x5555555551d2) resides in
.text, while the object layout remains 16 bytes.
<code>#include <iostream>
class Basic {
public:
int a;
double b;
void setB(double value) { b = value; }
};
int main() {
Basic temp;
temp.a = 10;
temp.setB(3.14);
return 0;
}</code>The
thispointer (passed as the first argument) lets the method access members via a fixed offset (e.g.,
0x8(%rax)for
b).
3. Private members and static members
Private data members are laid out exactly like public ones; they occupy consecutive slots in the object. Private methods also reside in
.textand can be invoked via a function‑pointer trick, though this bypasses compile‑time access control and is unsafe.
<code>#include <iostream>
class Basic {
public:
int a;
double b;
void setB(double value) { b = value; secret(b); }
private:
int c;
double d;
void secret(int temp) { d = temp + c; }
static void (Basic::*getSecretPtr())(int) { return &Basic::secret; }
};
int main() {
Basic temp;
temp.a = 10;
temp.setB(3.14);
// invoke private method via pointer
void (Basic::*funcPtr)(int) = Basic::getSecretPtr();
(temp.*funcPtr)(10);
return 0;
}</code>Static data members are stored once in the program’s data segment (
.datafor initialized values,
.bssfor zero‑initialized). Their addresses differ greatly from instance addresses.
4. Inheritance without virtual functions
When a class
Derivedinherits from
Basic, the object layout places the base‑class subobject first, followed by the derived members. The total size is the sum of both parts, and the layout is contiguous.
<code>#include <iostream>
class Basic { public: int a; double b; void setB(double v){b=v;} };
class Derived : public Basic { public: int c; void setC(int v){c=v;} };
int main(){
Derived obj;
obj.a = 10; obj.setB(3.14); obj.c = 1; obj.setC(2);
return 0;
}</code>5. Inheritance with virtual functions
Introducing virtual functions adds an 8‑byte vptr at the beginning of each polymorphic object. The vptr points to a vtable that holds addresses of the virtual functions. GDB shows the vptr (
0x555555557d80) and the two entries for
printInfoand
printB.
<code>#include <iostream>
class Basic {
public:
int a; double b;
virtual void printInfo(){ std::cout << "Basic" << std::endl; }
virtual void printB(){ std::cout << "Basic in B" << std::endl; }
void setB(double v){ b=v; }
};
class Derived : public Basic {
public:
int c;
void printInfo() override { std::cout << "Derived" << std::endl; }
void setC(int v){ c=v; }
};
int main(){
Derived d; d.a=10; d.setB(3.14); d.c=1; d.setC(2);
Basic* p=&d; p->printInfo(); p->printB();
return 0;
}</code>Polymorphic calls work only through pointers or references because the vptr is part of the actual object; copying a derived object into a base‑type object slices away the vptr.
6. Address‑Space Layout Randomization (ASLR)
Linux randomizes the virtual address space of each process (ASLR) to mitigate memory‑corruption attacks. GDB disables ASLR by default; enabling it with
set disable‑randomization offmakes object addresses vary between runs.
<code>(gdb) set disable-randomization off</code>7. Summary
Object memory is contiguous; members follow declaration order with compiler‑determined padding.
Member functions live in the text segment and do not affect object size.
Private members have no runtime protection; they can be accessed via raw offsets.
Static members reside in the data or BSS segment, shared across instances.
Inheritance typically places base members before derived members.
Polymorphic classes contain a vptr; virtual calls resolve through the vtable.
ASLR randomizes process addresses; GDB can toggle this behavior.
Tencent Architect
We share technical insights on storage, computing, and access, and explore industry-leading product technologies together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.