Reputation: 1360
Please read the following C++ code and the results. According to some wiki pages, static, automatic and dynamically-allocated variables are allocated in different address spaces, namely, data segment, stack and heap. However, it seems to me that the address of static and dynamic variables are at about the same place. Why is that so? How do I know the static variable is really in data segment, not in heap?
A more broad question is whether it is possible in C++ to know the range (or available size) of each address space?
Another question I have is why the address of volatile variable is 1?
#include <iostream>
using namespace std;
static int i;
int main() {
cout << sizeof(int*) << endl;
int j;
int* k = new int[10];
volatile int l;
cout << &i << endl;
cout << &j << endl;
cout << k << endl;
cout << &l << endl;
delete[] k;
}
Results:
8
0x1000010e4
0x7fff5fbff66c
0x100100080
1
Upvotes: 2
Views: 3185
Reputation: 21597
You might see a better separation of the data and stack segments if you make a bigger stack, with a recursive call. As other folks have pointed out, they are all part of the same "address space", but usually in different neighborhoods.
#include <iostream>
using namespace std;
int print_addresses(int depth) {
int j;
int* k = new int[10];
volatile int l;
static int i;
cout << "&i = " << &i << " "
<< "&j = " << &j << " "
<< "k = " << k << " "
<< "&l = " << (int *)&l
<< endl;
if (depth < 10)
print_addresses(depth + 1);
delete[] k;
}
int main() {
cout << sizeof(int*) << endl;
print_addresses(0);
}
On a x86-64 linux machine, I get the following output. It shows that the static variables have the same addresses. The addresses of stack variable increases as I make more function calls, but the addresses of the heap allocated variables decrease. This behavior is compiler and platform specific, so you shouldn't rely on this behavior. You can get the address of the volatile by casting its pointer it into an int pointer.
sizeof(int*)=8
&i = 0x6011a4 &j = 0x7fff9f67bb8c k = 0x15e7010 &l = 0x7fff9f67bb88
&i = 0x6011a4 &j = 0x7fff9f67bb5c k = 0x15e7040 &l = 0x7fff9f67bb58
&i = 0x6011a4 &j = 0x7fff9f67bb2c k = 0x15e7070 &l = 0x7fff9f67bb28
&i = 0x6011a4 &j = 0x7fff9f67bafc k = 0x15e70a0 &l = 0x7fff9f67baf8
&i = 0x6011a4 &j = 0x7fff9f67bacc k = 0x15e70d0 &l = 0x7fff9f67bac8
&i = 0x6011a4 &j = 0x7fff9f67ba9c k = 0x15e7100 &l = 0x7fff9f67ba98
&i = 0x6011a4 &j = 0x7fff9f67ba6c k = 0x15e7130 &l = 0x7fff9f67ba68
&i = 0x6011a4 &j = 0x7fff9f67ba3c k = 0x15e7160 &l = 0x7fff9f67ba38
&i = 0x6011a4 &j = 0x7fff9f67ba0c k = 0x15e7190 &l = 0x7fff9f67ba08
&i = 0x6011a4 &j = 0x7fff9f67b9dc k = 0x15e71c0 &l = 0x7fff9f67b9d8
&i = 0x6011a4 &j = 0x7fff9f67b9ac k = 0x15e71f0 &l = 0x7fff9f67b9a8
Upvotes: 0
Reputation: 20037
The fact that the language allows variables to be placed in different memory areas doesn't require any particular compiler/operating system to actually make those areas separate and distinct (the compiler does have to follow the correct language rules, e.g., memory allocated by new
must be deallocated by delete
, although it can be physically close to memory allocated other ways). For instance, the Standard makes a distinction between memory allocated by malloc
(officially allocated from the heap) and memory allocated by new
(officially allocated from the free store); but I've never seen an implementation put those in different memory segments.
Another question I have is why the address of volatile variable is 1?
The variable's address isn't 1. However, std::iostream
(e.g., std::cout
) doesn't have an overload for volatile
pointers, and the closest legal overload is for bool
. So outputting the address of a volatile
pointer will output 0
(if the pointer is NULL
) or 1
(all other cases).
A more broad question is whether it is possible in C++ to know the range (or available size) of each address space?
It may be possible on particular platforms, but there isn't any cross-platform way to do it.
Upvotes: 1
Reputation: 1827
local variables allocated in functions take space on stack (otherwise recursive functions wouldn't work). but
int* k = new int[10];
will make a call to malloc at some point, because 'new' allocates from global storage. So *i in your code although is local variable, points to global space.
Global variables and variables with file scope (ones that are not in function - like 'static int i') are in data section (or in so called bss if it doesn't define any value and it's platform dependant). On some platforms it happens that small chunk of data are in code section. Data section is part of file.
Upvotes: 1
Reputation: 168786
You are asking about your process's memory map, or the order and location of memory segments. This varies among the different execution environments (the WIN32 memory map is necessarily different than the Linux memory map), between different versions (the XP memory map may be different than the Windows7 memory map), and different CPU modes (obviously x86 and x86-64 differ).
Even with all other variables being equal, the memory map can even differ from run to run of the same program.
You might google "win32 memory map" and "linux process memory map" for more details about your environment. (Note: this is not the same subject as "memory-mapped files".)
It is not possible (in any portable way) to determine the range and size (or even the number or existence) of the various memory segments. C++ places no requirement, for example, that static data addresses aren't interspersed with dynamic data address.
For Linux, consult the pmap command. For Windows, try one of the sysinternals tools.
Finally, the address of your volatile variable is not, in fact, 1
, but rather std::cout
is printing it that way. See Why does std::cout convert volatile pointers to bool? for more information on that.
Upvotes: 3
Reputation: 47468
Only the OS can tell you which sections are in which parts of the address space. If you're using Linux, From within the program, output the contents of /proc/self/maps
:
I've added
std::ifstream maps("/proc/self/maps");
std::cout << maps.rdbuf();
at the end of your program, and it prints:
8
0x6021c0
0x7fffe07f60bc
0x603010
1
...
00601000-00602000 r--p 00001000 09:01 9175691 /home/cubbi/test
^ -- read-only static data
00602000-00603000 rw-p 00002000 09:01 9175691 /home/cubbi/test
^^ -- writeable static data
00603000-00624000 rw-p 00000000 00:00 0 [heap]
...
7fffe07d7000-7fffe07f9000 rw-p 00000000 00:00 0 [stack]
As for printing the address of a volatile int
, there is no standard operator<<
that takes a pointer-to-volatile-T, but there is one that takes bool
, and any pointer can be implicitly converted to void*
, which then can be converted to bool
. To print the address you want, change that line to
cout << const_cast<int*>(&l) << endl;
Upvotes: 8
Reputation: 477464
This is all very platform dependent, but here's how I understand it: There are three relevant segments, text, data, and stack. The text segment contains the code. The data segment contains the static variables. The stack segment contains the heap and the stack, and they fill the stack segment from opposite ends:
| Text | Data | ---> Heap Stack <--- |
i k j
Since the data size is known at compile time, I imagine that the stack segment will follow right after it, so the first heap allocation should come just after the last static variable. The first stack allocation, on the other hand, is as far away as possible, determined by the size of the stack segment.
Upvotes: 4
Reputation: 272687
Your variables will all be in the same address space, by definition.
But they may be in different sections (or segments) of this, depending on whether they're static, local (automatic) or dynamic.
Upvotes: 4