3

I have been reading about how memory is allocated in C++.

A few resources to mention:

http://www.geeksforgeeks.org/memory-layout-of-c-program/

http://duartes.org/gustavo/blog/post/anatomy-of-a-program-in-memory

Object creation on the stack/heap?

Global memory management in C++ in stack or heap?

http://msdn.microsoft.com/en-us/library/vstudio/dd293645.aspx

Heap / Stack and multiple processes

Do different programs gets their memory from a common heap or from a separate heap?

http://computer.howstuffworks.com/c28.htm

enter image description here

I want to clarify a few points based on my reading:

According to http://www.geeksforgeeks.org/memory-layout-of-c-program/ Section 4 Stack "Stack, where automatic variables are stored, along with information that is saved each time a function is called"

Suppose:

class myClass{
  int a;
  char b;
  public:
  myClass(int a,char b)
  {
   this->a = a;
   this->b = b;
  } 
};

1)According to what I have read, when we compile this code the binary sits in program memory and nothing has been allocated on stack yet. Correct?

Now in my main:

int main()
{
 myClass Ob(1,'c');
 return 0;
} 

2) Now an object Ob of size 5 bytes (4 bytes (int), 1 byte (char) - 32 bit OS) is created on stack, since it is an automatic variable. Correct ?

3) When the constructor myClass(int a,char b) is called do the temporary variables (parameters a, b) are created on stack for constructor and then destroyed after creating the object Ob? Like when we call a function by passing parameters by value.

Now suppose another class

class pointerClass {
 int a;
 char* b;
 public:
 pointerClass(int size){
 b= new char[size];
 a=size;
 }
}; 

Now in main :

int main()
{
 pointerClass ptr(10) ; //Step 1
}

4) Does this mean ptr object of size 8 bytes ( int a (4 bytes) , char* b (4 bytes i.e. this is just holding an address pointing to heap ) is created on stack ? Further a memory of 10 bytes (corresponding to new char[10] is allocated on heap) which is being pointed by the content of char* b? Am I correct?

5)When we pass a parameter to a function by reference such as fn (int *a,char* b) or fn(int& a,char& b) does this mean that a temporary pointer/reference is created on stack for the function which points to the actual object being passed and destroyed when the function returns? or rather the actual object is passed instead of creating and destroying a temporary pointer/reference on stack for the function?

This I asked yesterday but I am not satisfied with the answer: Constructor, Copy Constructor and Stack Creation : C++

6)When we overload a fn such as fn(int a,char b) fn(int& a,char& b) we can call from main as fn(A,B) with below cast static_cast<void(*)(int, char)>(fn)(a, c); //Calls fn(int a,char b) static_cast<void(*)(int&, char&)>(fn)(a, c);//Calls fn(int& a.char& b) What exactly is happening here ? What is void (*) .

Thanks

Community
  • 1
  • 1
Gaurav K
  • 2,864
  • 9
  • 39
  • 68
  • 7
    I think you need to divide this question to several small questions. Do you expect us to write C++ book as an answer? – Alex F May 07 '13 at 06:16
  • I think all questions are related, hence put under one question. And the answer is 'yes' or 'no' mostly .. So not much to type while replying .. – Gaurav K May 07 '13 at 06:18
  • 1
    Not really a programming question either if you ask me. Just a "I think it works that way, is that OK" kind of question. And according to john's answer, he seems to be correct on all questions. – Bart Friederichs May 07 '13 at 06:21
  • 2
    I think C++ (as a language defined by the Standard) is not as tightly coupled to the memory model of the computer where the program is run on as you might think. E.g. defining a variable in C++ does not necessarily mean that anything is changed on the stack. – dyp May 07 '13 at 06:26
  • "or rather the actual object is passed" How should that work? Passing arguments is based on copying them to pre-defined stack locations relative to a stack pointer (frame pointer IIRC). – dyp May 07 '13 at 06:35
  • 1
    This really isn't a good fit to SO. SO is for specific practical programming questions, not learning about operating system and language design from the ground up. – Raymond Chen May 07 '13 at 06:42

3 Answers3

6
  1. Correct - Allocations happen at run-time.
  2. Partially Correct - The standard does not use the terms stack and heap, it merely demands behaviors from objects. However, a stack is the most common and usual way to achieve this functionality. Also, compilers are allowed to pad structures with padding bytes so the size of the object shouldn't be speculated. This is known as structure padding. Simply, use sizeof to get the size.
  3. Partially Correct - Pass and return by value does need a copy constructor to be accessible calls, but these calls maybe elided under circumstances. The process is known as copy elision.
  4. Partially Correct - The pointer just points to an object with a dynamic storage. Size of pointer may vary though.
  5. The pointer or reference is created local to function but it points or refers to the object whose address is passed. There is no copy required here and none happens.
  6. Every variable has a data type in C and C++. Typecasting allows you the flexibility to force the compiler to treat a pointer to one data type to be treated as pointer to completely different data type. Since functions have a type, pointers to functions have a type too and typecasting allows you to force compiler to treat a function pointer from one function type to completely another type, thus essentially allowing you to call the desired overloaded function version.
Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • "Size of pointer may vary though." may vary depending on C++ implementation (i.e. compiler, target platform etc.) but constant within a compiled program. – dyp May 07 '13 at 06:34
  • @juanchopanza: Hence the *Partially correct* on Point 3. Clarified it to remove the confusing wording. – Alok Save May 07 '13 at 06:35
  • Well I like this answer, so +1, but I have some remarks: "Allocations happen at run-time" there's load-time and the initialization of structures required for the Library (e.g. some stateful C string functions). I asked about "dynamic memory" because I only know from the Std "dynamic storage duration" (-> new, delete) but pointers can also point to objects of static or automatic (or thread local) storage duration. 6) the conversion is required for overload resolution. – dyp May 07 '13 at 06:42
  • 1
    Note on (3): There is no requirement that parameters be passed on the stack. Many processors pass the first N parameters in registers. – Raymond Chen May 07 '13 at 06:44
  • The last sentence sounds like a `reinterpret_cast`, enforcing UB. It's a default use case for conversions to chose the overload for an overloaded function name, not an indirect effect IMO. – dyp May 07 '13 at 06:53
  • Re 2: the standard may not use the word "stack" in this context, but it definitely requires a stack. (How the stack is implemented is another question: I once used an implementation of C which implemented it by means of a linked list, with each stack frame allocated on the heap.) – James Kanze May 07 '13 at 08:58
4
  1. correct
  2. correct (although it may not be five bytes, probably eight)
  3. correct
  4. correct
  5. temporary pointer/reference is created on the stack, not sure why you weren't happy with the answer given previously it looks correct to me
  6. void(*)(int,char) is a type, specifically a pointer to a void function taking two arguments and int and a char. Evidently this cast forces the compiler to choose the version of the function you want, although it's news to me that this was possible.

Of course must add the obligatory warning that nothing in the above is required for C++, it's just how C++ is typically implemented.

john
  • 85,011
  • 4
  • 57
  • 81
  • Just concerning the first question: I'd say it largely depends on how you define "stack" and some others terms. "1)According to what I have read, when we compile this code the binary sits in program memory and nothing has been allocated on stack yet." compiling != loading the program (or executing) and so on – dyp May 07 '13 at 06:22
  • john.. Regarding 5: The reply I got was no temporary reference or pointer is created on stack rather the object is passed directly... Can you pass me any reference to read if u knw any for this ? – Gaurav K May 07 '13 at 06:24
  • 2
    @GauravK I think you misread the answer 'In either case the copy of the object is **not** created.' (my emphasis). – john May 07 '13 at 06:26
  • @john Ok .. it means temporary references/pointers are created which point to passed object and no copy of object is created on the stack.. and that is also the reason why copy constructor is not called... – Gaurav K May 07 '13 at 06:28
3

First, I should point out that the diagram you show is very system dependent. Under Solaris, for example, the operating system memory isn't mapped at all into the user address space. And the most frequent mapping has just three mapped segments for user memory, at least at the beginning of the program: a code segment at the bottom (but not the absolut bottom, because the address 0 will normally not be mapped), a data segment above it, and a stack segment (which grows down) at the top, with a big hole of unmapped memory between the stack and the data.

All of which changes completely as soon as you start dynamically loading.

  1. No. After you comple a code (in the larger sense of compile), the executable is in a file, not in memory. The program doesn't get loaded into memory until you execute it. (There used to be some exceptions in early Unix, and in embedded systems, even today. But for general purpose systems like Windows and modern Unix, this is true.)

  2. The variable will be created on the stack. But it will almost certainly be larger than 5 bytes, due to alignment considerations. (Eight bytes would be the minimum for most 32 bit machines.) In C++, object creation is a two step process: memory allocation and calling the constructor. In most implementations, all of the memory that will be necessary for all of the objects used in the function will be allocated immediately, at the top of the function; in some cases, extra memory will also be allocated on either side of each variable, for debugging reasons, and the memory will be initialized. The constructor for the object will be called when program flow passes the definition of the object.

  3. Yes. Calling a constructor is exactly like calling an any other function. Again, creation of the arguments is a two step process; in this case, strategies vary: some implementations will allocate all of the memory needed for any arguments at the top of the function, others will allocate them on an as needed basis, just before initializing them. And in the case of simple variables, like int, most machines have a single instruction which will allow allocating and initialization in the same instruction. Depending on the types of the arguments, and how they are initialized, the compiler may even use different strategies.

  4. Correct, more or less. For built in types like int or pointers, the only "destruction" is freeing the memory, and depending on the compiler, that might not happen until the end of the calling function. (On the other hand, if you call a second function, this memory will be reused. So the program behaves exactly "as if" the memory had been freed immediately.)

  5. Correct, more or less. (Formally, references aren't "destroyed", because they aren't objects. Practically, at least when they are used as function parameters, the underlying code is exactly the same as for pointers.)

  6. First, the only thing you can do with the results of a static_cast on a pointer to a function is to cast it back to its original type. Anything else is undefined behavior. If fn is defined as void fn( int a, char b ), using the results of static_cast<void (*) ( int&, char& )>( fn ) is undefined behavior, and will not work. What exactly happens here can be just about anything, but there's a good chance that it will make the program crash. And void (*) in this case is part of the declaration of a pointer to function type; void (*)( int, char ), is the name of the type: pointer (the *) to function (the ( int, char )— the parentheses are necessary because of the precedence rules) returning void.

EDIT:

Just a correction concerning point 6. I'd missed the fact that the function was overloaded for both types. A static_cast can be used to resolve function overloading like this: in such cases, the usual rules don't apply, since there is no type conversion. (And yes, this is very confusing.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329