0

Question Summarized:

I'm well aware how when you enter a new scope, variables get "added onto the stack" but I guess my question is how exactly are objects represented on the stack on a lower-level?

How are an object's member variables represented? Is an object represented as just a literal sequential set of member variables added? What about if a member variable is a whole other object?

What about member functions? Are they just an address to the function? So we add a pointer to the function definition? How does the system go from that to execution if it's called?


Example:

Let's say I have a class defined as follows:

class MyClass {
    int a;         // Primitive data member
    double b;      // Primitive data member
    std::unordered_map<int, int>* some_map; // A pointer to an object
    SomeOtherClass sub_obj; // Another object as a data member 

    void DoSomething() {
       // some code ...
    }
};

void function(const MyClass& obj) {
    // Run some operations on obj
}

And I call function, and obj is added to the stack. What is "stacked" exactly?

PS Linking any resources for further reading about this nitty-gritty kind of thing would be much appreciated!

JoeVictor
  • 1,806
  • 1
  • 17
  • 38
  • 5
    Often, if the variables are small enough, the compiler doesn't need to use the stack and just stores the variables in registers. I recommend looking at the assembly generated by your compiler, either directly or with a tool like [Compiler Explorer](https://godbolt.org). Your question right now is kind of vague because the details depend on exactly what architecture you are using, and it might depend on compiler's optimization settings and whether the compiler can inline the function. – David Grayson Aug 07 '23 at 20:11
  • 3
    Why do you care? When writing C++ day in and day out to achieve a goal, it hardly matters. – Jesper Juhl Aug 07 '23 at 20:12
  • 3
    Your question is a bit too broad, and subject to each compiler's particular implementation. Any [decent C++ book](https://stackoverflow.com/questions/388242/) should be able to cover the basic concepts if you really want more info. – Remy Lebeau Aug 07 '23 at 20:15
  • 2
    There's an old book that covers this kind of thing, 'Inside the C++ Object Model' by Stanley B. Lippman – john Aug 07 '23 at 20:16
  • 4
    You made the final question a lot simpler than the leadup. The parameter `obj` is a reference, so what gets passed is an address (just like for a pointer). That assumes that the compiler isn't doing any cross-procedure optimization (such as inlining) involving that call. And parameters aren't necessarily passed on the stack, that depends on the calling convention. – Ben Voigt Aug 07 '23 at 20:19
  • Implementations vary. The standard C++ presumes the code's behavior (for where it has defined behavior) to be running on an *abstract C++ machine*, and that machine is not specified as having a stack (it has **automatic** storage, and **dynamic** storage). The **stack** is an implementation detail. – Eljay Aug 07 '23 at 20:20
  • @BenVoigt Yes, I guess I implicity assumed no optimizations or inlining. What do you mean by "depends on the calling convention"? – JoeVictor Aug 07 '23 at 20:21
  • 2
    https://en.wikipedia.org/wiki/Calling_convention#Different_calling_conventions – Ben Voigt Aug 07 '23 at 20:22
  • 3
    @JoeVictor _Calling conventions_ are the way functions get called, how parameters get passed to them, and how they communicate their return value back to the caller. For instance, in the x86 cdecl calling convention all parameters are passed by pushing them onto the stack in reverse order. Other calling conventions (including both major x86_64 conventions) may pass parameters by placing them in specific CPU registers (or some combination of registers and the stack, if there's too many parameters or they're too big for registers). – Miles Budnek Aug 07 '23 at 20:25
  • To all who provided further reading -- these resources are great, thanks! Exactly the specifics I was looking to get into. Already ordered 'Inside the C++ Object Model', 'Exceptional C++' and playing around with Compiler Explorer now – JoeVictor Aug 07 '23 at 20:30
  • My IAR Electronic Workbench compiler decided to use two registers (ARM compiler) to represent a `std::pair` structure. No stack was needed. No memory used. – Thomas Matthews Aug 07 '23 at 20:44
  • 1
    See also [How to remove "noise" from GCC/clang assembly output?](https://stackoverflow.com/q/38552116) and Matt Godbolt's CPPCon talk (linked from there) for more. – Peter Cordes Aug 07 '23 at 20:49
  • 1
    When a new statement block is encountered, the compiler decides whether to reuse registers, use unused registers or push stuff on the stack. The C++ language does not state the detailed requirements, but leaves a lot of discretion for the compiler. At first pass, the compiler may choose to use the stack, while at higher optimizations, the compiler may choose to use registers. The compiler may choose to use L3 or other fast memory instead of the stack. – Thomas Matthews Aug 07 '23 at 20:50
  • 2
    @ThomasMatthews: None of the mainstream compilers (GCC/Clang/MSVC/ICC) will move the stack pointer when entering a new scope (except for VLAs), even in a debug build. The stack pointer normally only moves once, in the prologue. (Possibly after checking for an early-out condition; that's "shrink-wrap" optimization to avoid stack-frame setup in simple cases.) Also, L3 cache is transparent; software can't choose to use it or not. (A few microarchitectures have "scratchpad" memory that's fast like cache but has its own address. e.g. Xeon Phi could config its EDRAM that way.) – Peter Cordes Aug 07 '23 at 20:58

0 Answers0