2011-09-20 78 views
2

请阅读以下C++代码和结果。根据某些wiki页面,静态,自动和动态分配的变量分配在不同的地址空间中,即数据段,堆栈和堆。但是,在我看来,静态和动态变量的地址大致在同一个地方。为什么?我如何知道静态变量确实在数据段中,而不是在堆中?关于C++中地址空间的问题

更广泛的问题是,在C++中是否可以知道每个地址空间的范围(或可用大小)?

我的另一个问题是为什么volatile变量的地址是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; 
} 

结果:

8 
0x1000010e4 
0x7fff5fbff66c 
0x100100080 
1 
+5

每个进程都有*一个*地址空间。你正在混合地址空间的部分/段。 –

+0

然后我的问题变成:我们可以用C++知道每个段(或段)的范围(或大小)是什么? –

+1

如果你的可修改全局变量接近用new分配的对象,那可能是因为加载器或程序自己的init代码(在调用'main'之前)为它们分配了一块内存,并且该块最终来了来自与'new'使用相同的来源。 –

回答

8

只有操作系统可以告诉你哪些部分位于地址空间的哪一部分。如果你在程序中使用Linux,从,输出的/proc/self/maps内容:

我在你的程序的末尾添加

std::ifstream maps("/proc/self/maps"); 
std::cout << maps.rdbuf(); 

,并印刷:

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] 

作为用于打印volatile int的地址,没有标准operator<<,需要一个指针到挥发性-T,但有一个取bool,任何指针可以被隐式转换为void*,WH然后可以将其转换为bool。要打印您需要的地址,把上面一行

cout << const_cast<int*>(&l) << endl; 
4

你的变量都将在同一个地址空间,顾名思义。

但是它们可能在不同的部分(或部分),这取决于它们是静态的,本地的(自动的)还是动态的。

4

这都是非常依赖于平台的,但是这里是我的理解:有三个相关的段,文本,数据和堆栈。文本段包含代码。数据段包含静态变量。堆栈段包含堆和栈,并且他们填写从相对端堆栈段:

| Text | Data | ---> Heap  Stack <--- | 
      i k       j 

由于数据大小是在编译时已知的,我想,堆栈段将右后它遵循,这样第一个堆分配应该在最后一个静态变量之后。另一方面,第一堆栈分配尽可能远,由堆栈段的大小决定。

3

您在询问您的进程的内存映像或内存段的顺序和位置。这在不同的执行环境(WIN32内存映射必然不同于Linux内存映射),不同版本(XP内存映射可能不同于Windows7内存映射)和不同CPU模式(显然是x86和x86- 64个不同)。

即使所有其他变量相同,内存映射甚至可能会不同于同一程序的运行。

有关您的环境的更多详细信息,您可以google“win32内存映射”和“linux进程内存映射”。 (注意:这与“存储器映射文件”不同。)

不可能(以任何便携的方式)确定各种存储器的范围和大小(甚至数量或存在)段。 C++没有要求,例如,静态数据地址不与动态数据地址交错。对于Linux,请查阅pmap command。对于Windows,请尝试使用sysinternals工具之一。

最后,你的volatile变量的地址其实不是,1,而是std::cout就是这样打印的。有关更多信息,请参阅Why does std::cout convert volatile pointers to bool?

1

的语言允许变量被放置在不同的存储区域不要求任何特定的编译器/操作系统实际上使这些地区分离的事实而不同(编译器必须遵循正确的语言规则,例如,通过new分配的内存必须由delete被释放,虽然它可以是物理上接近的内存分配等方式)。例如,通过malloc(从堆正式分配的)分配的存储器和存储器之间的Standard makes a distinction通过new(从自由存储区分配正式)分配;但我从来没有见过一个实现把它们放在不同的内存段中。

我的另一个问题是为什么volatile变量的地址是1?

变量的地址不是1。然而,std::iostream(例如,std::cout)不具有用于volatile指针的过载,并且最接近的合法过载是用于bool。所以输出volatile指针将输出0(如果指针是NULL)或1的(所有其他情况下)中的地址。

一个更广泛的问题是,在C++中是否有可能知道每个地址空间的范围(或可用大小)?

它可能在特定的平台上,但没有任何跨平台的方式来做到这一点。

0

您可能会看到的数据和堆栈段更好地分离,如果你犯了一个更大的堆栈,用递归调用。正如其他人所指出的,它们是相同的“地址空间”的组成部分,但通常在不同的街区。

#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); 
} 

在x86-64 linux机器上,我得到以下输出。它显示静态变量具有相同的地址。堆栈变量增加的地址,因为我赚更多的函数调用,但堆中分配的变量的地址减少。这种行为是编译器和平台特定的,所以你不应该依赖这种行为。您可以通过将其指针转换为int指针来获取volatile的地址。

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 
1

在函数中分配的局部变量占用堆栈空间(否则递归函数将不起作用)。但

int* k = new int[10]; 

将在某个时候调用malloc,因为“新”,从全局的存储分配。所以*我在你的代码中虽然是局部变量,但指向全局空间。

具有文件范围的全局变量和变量(不在函数中的变量 - 例如'static int i')在数据段中(或者在没有定义任何值且与平台相关的情况下称为bss)。在某些平台上,小块数据位于代码段。数据部分是文件的一部分。