C ++中的内存组织[关闭]

Posted

技术标签:

【中文标题】C ++中的内存组织[关闭]【英文标题】:Memory Organization in C++ [closed] 【发布时间】:2013-05-01 00:15:47 【问题描述】:

我一直在阅读有关 C++ 中如何分配内存的信息。

需要提及的一些资源:

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

我想根据我的阅读澄清几点:

根据http://www.geeksforgeeks.org/memory-layout-of-c-program/ 第 4 节堆栈“堆栈,其中存储自动变量,以及每次调用函数时保存的信息”

假设:

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

1)根据我读到的内容,当我们编译这段代码时,二进制文件位于程序内存中,堆栈上还没有分配任何内容。对吗?

现在主要是:

int main()

 myClass Ob(1,'c');
 return 0;
 

2) 现在在堆栈上创建一个大小为 5 字节(4 字节 (int),1 字节 (char) - 32 位 OS)的对象 Ob,因为它是一个自动变量。对吗?

3) 当构造函数myClass(int a,char b) 被调用时,临时变量(参数a,b)是否在构造函数的堆栈上创建,然后在创建对象Ob 后销毁?就像我们通过按值传递参数来调用函数一样。

现在假设另一个类

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

现在主要:

int main()

 pointerClass ptr(10) ; //Step 1

4) 这是否意味着在堆栈上创建大小为 8 字节的 ptr 对象(int a(4 字节)、char* b(4 字节,即它只是保存指向堆的地址)?此外还有 10 字节的内存(对应于new char[10] is assigned on heap)是char* b的内容指向的那个?我说的对吗?

5) 当我们通过引用(例如fn (int *a,char* b)fn(int& a,char& b))将参数传递给函数时,这是否意味着会在堆栈上为函数创建一个临时指针/引用,该函数指向被传递和销毁的实际对象函数何时返回?还是传递实际对象而不是在堆栈上为函数创建和销毁临时指针/引用?

这是我昨天问的,但我对答案不满意: Constructor, Copy Constructor and Stack Creation : C++

6) 当我们重载 fn 时,例如 fn(int a,char b) fn(int& a,char& b) 我们可以从 main 调用 fn(A,B) 有以下演员表 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) 这里到底发生了什么?什么是无效(*)。

谢谢

【问题讨论】:

我觉得你需要把这个问题分成几个小问题。你希望我们写 C++ 书作为答案吗? 我认为所有问题都是相关的,因此放在一个问题下。答案主要是“是”或“不是”..所以在回复时输入的内容不多.. 如果你问我,这也不是一个真正的编程问题。只是一个“我认为它可以这样工作,可以吗”之类的问题。根据约翰的回答,他似乎在所有问题上都是正确的。 我认为 C++(作为标准定义的语言)与运行程序的计算机的内存模型并不像您想象的那样紧密耦合。例如。在 C++ 中定义变量并不一定意味着堆栈上的任何内容都发生了变化。 这确实不适合 SO。 SO 是针对具体的实际编程问题,而不是从头开始学习操作系统和语言设计。 【参考方案1】:
    正确 正确(虽然可能不是五个字节,可能是八个) 正确 正确 临时指针/引用是在堆栈上创建的,不知道为什么您对之前给出的答案不满意,它看起来对我来说是正确的 void(*)(int,char) 是一种类型,特别是指向带有两个参数和一个 int 和一个 char 的 void 函数的指针。显然,这种强制转换迫使编译器选择您想要的函数版本,尽管这对我来说是个新闻。

当然必须添加强制性警告,即 C++ 不需要上述任何内容,这正是 C++ 的典型实现方式。

【讨论】:

仅关于第一个问题:我想说这在很大程度上取决于您如何定义“堆栈”和其他一些术语。 “1)根据我所读到的,当我们编译这段代码时,二进制文件位于程序内存中,堆栈上还没有分配任何内容。”编译!=加载程序(或执行)等等 john.. 关于 5:我得到的答复是没有临时引用或指针是在堆栈上创建的,而是直接传递对象...如果你知道的话,你能把任何参考传递给我吗?这个? @GauravK 我认为您误读了答案“在任何一种情况下,对象的副本都是创建的。” (我的重点)。 @john Ok .. 这意味着创建了指向传递对象的临时引用/指针,并且没有在堆栈上创建对象的副本.. 这也是不调用复制构造函数的原因...【参考方案2】:
    正确 - 分配发生在运行时。 部分正确 - 该标准不使用术语堆栈和堆,它仅要求对象的行为。但是,堆栈是实现此功能的最常见和最常用的方法。此外,允许编译器使用填充字节填充结构,因此不应推测对象的大小。这称为 structure padding。只需使用sizeof 即可获取大小。 部分正确 - 按值传递和返回确实需要复制构造函数才能进行可访问调用,但在某些情况下可能会省略这些调用。该过程称为 copy elision。 部分正确 - 指针仅指向具有动态存储的对象。但指针的大小可能会有所不同。 指针或引用是在函数本地创建的,但它指向或引用其地址被传递的对象。这里不需要副本,也不会发生任何事情。 在 C 和 C++ 中,每个变量都有一个数据类型。类型转换允许您灵活地强制编译器将指向一个数据类型的指针视为指向完全不同的数据类型的指针。由于函数具有类型,指向函数的指针也具有类型,并且类型转换允许您强制编译器将函数指针从一种函数类型完全处理为另一种类型,从而实质上允许您调用所需的重载函数版本。

【讨论】:

“指针的大小可能会有所不同。”可能因 C++ 实现(即编译器、目标平台等)而异,但在已编译的程序中保持不变。 @juanchopanza:因此第 3 点的部分正确。澄清它以消除令人困惑的措辞。 嗯,我喜欢这个答案,所以 +1,但我有一些评论:“分配发生在运行时”有加载时间和库所需的结构初始化(例如一些有状态的 C字符串函数)。我询问了“动态内存”,因为我只从标准中知道“动态存储持续时间”(-> new,delete),但指针也可以指向静态或自动(或线程本地)存储持续时间的对象。 6) 重载解析需要转换。 (3)注意事项:不要求参数传入栈。许多处理器在寄存器中传递前 N 个参数。 最后一句听起来像reinterpret_cast,强制执行UB。这是转换的默认用例,为重载函数名称选择重载,而不是间接影响 IMO。【参考方案3】:

首先,我应该指出,您展示的图表非常 系统依赖。例如,在 Solaris 下,操作 系统内存根本没有映射到用户地址空间。 最频繁的映射只有三个映射段 用户内存,至少在程序的开头:一段代码 底部的段(但不是绝对底部,因为 地址 0 通常不会被映射),它上面的数据段, 和一个堆栈段(向下生长)在顶部,有一个大的 堆栈和数据之间的未映射内存空洞。

一旦您动态启动,所有这些都会完全改变 加载。

    没有。完成代码后(在更大的意义上 compile),可执行文件在文件中,而不是在内存中。这 程序在您执行之前不会被加载到内存中。 (在早期的 Unix 中曾经有一些例外,在嵌入式 系统,即使在今天。但是对于通用系统,例如 Windows 和现代 Unix,这是真的。)

    变量将在堆栈上创建。但它几乎会 由于对齐,肯定大于 5 个字节 考虑因素。 (对于大多数 32 位机。)在 C++ 中,对象创建是一个两步过程: 内存分配和调用构造函数。多数情况 实现,所有的内存将是必要的 函数中使用的所有对象都将被分配 立即,在函数的顶部;在某些情况下,额外 内存也将分配在每个变量的两侧, 出于调试原因,内存将被初始化。 程序运行时将调用对象的构造函数 传递对象的定义。

    是的。调用构造函数与调用 任何其他功能。同样,参数的创建是一个两步过程; 在这种情况下,策略会有所不同:一些实现将 在顶部分配任何参数所需的所有内存 的功能,其他人将根据需要分配它们 基础,就在初始化它们之前。而在简单的情况下 变量,如int,大多数机器只有一条指令 这将允许在相同的情况下分配 初始化 操作说明。取决于参数的类型,以及如何 它们被初始化,编译器甚至可能使用不同的 策略。

    正确,或多或少。对于像 int 这样的内置类型或 指针,唯一的“破坏”是释放内存,并且 取决于编译器,这可能直到最后才会发生 的调用函数。 (另一方面,如果你打电话 第二个函数,这个内存将被重用。所以程序 行为完全“好像”内存已被立即释放。)

    正确,或多或少。 (正式地,引用不是 “销毁”,因为它们不是对象。实际上,至少 当它们用作函数参数时,底层代码 与指针完全相同。)

    首先,唯一你可以用结果做的事情 指向函数的指针上的static_cast 是将其转换回 它的原始类型。其他任何事情都是未定义的行为。如果 fn 定义为 void fn( int a, char b ),使用结果 static_cast<void (*) ( int&, char& )>( fn ) 未定义 行为,并且将不起作用。这里到底发生了什么可能是 几乎任何东西,但很有可能它会 程序崩溃。而void (*) 在这种情况下是 part 声明指向函数类型的指针; void (*)( int, char ),是类型的名称:指向函数的指针(*)( ( int, char )——括号是必要的,因为 优先规则)返回void

编辑:

只是对第 6 点的更正。我错过了这样一个事实 这两种类型的函数都被重载了。一个static_cast可以 用于解决这样的函数重载:在这样的 情况下,通常的规则不适用,因为没有类型 转换。 (是的,这非常令人困惑。)

【讨论】:

+1 以获得这个全面而准确的答案。

以上是关于C ++中的内存组织[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

C/C++动态内存创建与内存管理

Python:文件操作

C中的内存分配问题[关闭]

(Java)包组织有最佳实践吗? [关闭]

在命名空间中组织 PHPUnit 测试 [关闭]

C:数组基础