C++的内存模型

一、程序的编译

1.1、头文件内容

  • 函数的原型
  • 使用#define和const定义的符号常量
  • 结构声明
  • 类声明
  • 模板声明
  • 内联函数

1.2、头文件的包含

注意尖括号与双引号的区别

#include <test.h>

如上,如果文件名包含在尖括号中,则C++编译器将在存储标准头文件的主机系统文件系统内查找。

#include “test.h”

如上,如果文件名包含在双引号中,则编译器优先查找当前的工作目录或源代码目录,没有找到的情况下才会去查找系统标准文件内查找。

1.3、头文件单次编译管理

同一个头文件可能会在一个程序内被包含多次,需要使用一种标准的C/C++技术避免多次包含带来的问题。
即使用基于预处理器编译指令 #ifdef/#ifndef。

#ifndef TEST_H
#define TEST_H
...
#endif

编译器首次遇到该文件时,名称TEST_H并没有定义,此时编译器查看头文件的内容并定义TEST_H,后续在同一个文件中遇到其他包含该头文件的代码,编译器不会再次编译而选择忽略。,从而跳转到#endif后的一行。需要注意这种方法并没有避免一个头文件在同一个文件内被包含多次,只是让它忽略了第一次之后的内容。

二、存储持续性、作用域与链接性

C++(C++11)使用四种不同的方案来存储数据,区别在于数据保留在内存的的持续时间。

2.1、自动存储持续性

在函数定义中声明的变量(包括函数参数)的存储持续性为自动的,它们在程序开始执行其所属的函数或代码时被创建,在函数或代码块执行完时,它们使用的内存被释放。

2.2、静态存储持续性

在函数定义外定义的变量和使用关键字static定义的变量的存储持续性为静态的,它们在整个程序的运行过程中都存在。

2.3、线程存储持续性(C++11)

在多核处理器中,CPU可同时处理多个执行任务,这让程序可以将计算放在可并行处理的不同线程中,如果变量是使用关键字thread_local声明的,则其生命周期与所属的线程一样长。

2.4、动态存储持续性

用new运算符分配的内存将一直存在,直到使用delete运算符将其释放或程序结束为止,这种内存的存储持续性为动态的,有时候被称为自由存储或堆存储。

2.5、作用域

作用域:描述了名称在文件的多大范围内可见。
局部作用域:只能在定义它的代码块内可用,比如函数体。
全局作用域:在定义位置到文件结尾之间都可用。
静态变量的作用域是全局还是局部取决于是如何被定义的,是在函数体内定义的还是在类中声明的,或者是命名空间内声明的变量。

2.6、链接性

链接性:描述了名称如何在不同单元间共享。链接性为外部的名称可在文件间共享,内部的名称只能由一个文件中的函数共享,自动变量的名称没有链接性,因为他们不能被共享。

三、自动存储持续性

3.1、作用域与链接性

在默认情况下,在函数中声明的函数参数和变量的存储持续性为自动,作用域为局部,没有链接性。当程序开始执行这些变量所属的代码块时,将为其分配内存;当函数运行结束时,这些变量将全部消失。所以这些变量的存在时间合作用域都将被限制在该代码块内。
函数内外存在同名的变量时,程序执行到函数内部,函数内的局部变量会将全局变量隐藏,函数执行完成后全局变量将可见。

3.2、自动变量与栈

程序在运行时对自动变量进行管理,一般方法是留出一段内存,将其视为栈(LIFO:后进先出)。新数据放在原有数据的上面,当程序使用完后,将其从栈中删除。程序使用两个指针来跟踪栈,一个指针指向栈底-栈的开始位置,另一个指针指向栈顶-下一个可用的内存单元,函数调用时,函数内的自动变量将被加入到栈中,栈顶指针指向变量后一个可用的内存单元,函数结束时,栈顶指针重新指向函数执行前的位置,从而释放函数内自动变量使用的内存。
其中栈顶指针移动的多少取决于自动变量的类型。

3.3、寄存器变量

关键字register是由C语言引入的,它建议寄存器使用CPU寄存器来存储自动变量,旨在提高访问变量的速度;
register int count_fast;

四、静态持续变量

4.1、静态持续变量的三种链接性

外部链接性:必须在代码块的外面声明,作用域为整个文件,即从声明位置到文件结尾的范围内都可以被使用,也可在其他文件中访问。
内部链接性:必须在代码块的外面声明,并使用static限定符,可在当前文件中访问。
无链接性:只能在当前函数或代码块中访问,并使用static限定符。
静态变量的寿命更长,链接性存在于整个程序的执行期间。程序运行期间的静态变量数目也是固定不变的,编译器分配固定的内存块来存储所有的静态变量,如果不显式初始化,则编译器自动默认设为0。

...
int global =1000;                     // static duration, external linkage;
static int one_file =50; 		      // static duration, internal linkage;
int main()
{
	...
}
void funct1(int n)
{
	static int count = 0;			  // static duration, no linkage;
	int llama = 0;
	...
}

void funct2(int q)
{
	...
}

4.2、静态变量的初始化

零初始化:所有未被初始化的静态持续变量的所有位都被设置为0,对于标量类型,零将被强制转换为合适的类型。
静态初始化:常量表达式初始化和零初始化。是编译器处理文件(翻译单元)时初始化变量。
动态初始化:变量在编译后再初始化。比如变量在初始化时缺乏足够的信息,将被动态初始化。

long z = 13 * 13const double pi = 4.0 * atan(1.0);

初始化pi,必须调用atan(),这需要等到该函数被链接且程序执行时。

4.3、静态持续性与外部链接性

a、链接性为外部的变量通常简称为外部变量,存储持续性为静态,作用域为整个文件,可以在文件中位于外部变量定义之后的任何函数都可以使用它,因此也可以成为全局变量。
b、两种全局变量的声明方式:一种是定义,它给变量分配内存空间,另一种是引用声明,不给变量分配内存空间,因为它引用了已有的变量。如果需要在多个文件使用外部变量,只需要在一个文件内包含该变量的定义,但是在使用该变量的其他所有文件中,都必须使用关键字extern声明它。
c、默认情况下全局变量的链接性为外部的,但是const全局变量的链接性为内部的,全局const定义跟使用了static限定符一样。
d、处于某种原因,可以使用extern关键字来覆盖默认的内部链接性。

extern const int fingers = 50;

4.4、静态持续性与内部链接性

将static限定符用于作用域为整个文件的变量时,该变量的链接性将为内部的。
链接性为内部的静态变量将在同一个文件内的多个函数之间共享数据。

4.5、静态持续性与无链接性

将static限定符用于在代码块内定义的变量,在代码块内使用static时。将导致局部变量的存储持续性为静态的,虽然此时该变量只在该代码块内可用,但它在该代码块不处于活动状态时仍然存在,因此在两次函数调用之间,静态局部变量的值将保持不变。
静态局部变量初始化:程序只在启动时进行一次初始化,以后再调用函数时,将不会像自动变量那样再次被初始化。

4.6、全局变量与局部变量

选择:全局变量声明所属文件内的所有的函数都可以访问它,因此使用全局变量时不用传递参数,但缺点是此时的程序不可靠(使得数据完整性无法保证),通常情况下,尽量使用局部变量,在需要时才传递数据。

五、动态存储链接性

5.1、动态内存

C++运算符new分配的内存。由new和delete控制,不受作用域与链接性规则控制,因此可以在一个函数中分配内存,而在另一个函数中释放,与自动变量的LIFO不同,其内存的分配与释放要取决于new和delete在何时以何种方式被使用,new分配的内存将一直保留在内存中,直到使用delete运算符将其释放。(编译器使用三块独立的内存:一块用于静态变量,一块用于自动变量,一块用于动态存储。)

int *pi = new int (6);       //*pi set to 6
double *pd = new double (99.99);    // *pd set to 99.99

为系统内置的标准变量类型分配存储空间并初始化,在类型名后面加上括号和初始化的值;

struct where 
{
	double x;
	double y;
	double z;
};

where *one = new where {2.5, 3.5, 4.5};
int *ar =  new int[4] {2, 3, 4, 5};
要初始化结构体变量或数组,则需要使用大括号的列表初始化(C++11允许这么做)。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值