C++学习笔记 – 内存模型

单独编译

将程序可以分成三部分:

头文件中常包含的内容:函数原型;使用#define或const定义的符号常量;结构声明;类声明;模板声明;内联函数;

包含头文件时,如果文件名包含在尖括号中,则编译器将在存储标准头文件的主机系统的文件系统中查找;如果文件包含在双引号中,则编译器将首先查找当前的工作目录或源代码目录,如果没有找到,则将在标准位置查找。因此包含自己的头文件时,应使用引号而不是尖括号。

有一种标准的C/C++技术可以避免多次包含同一个头文件,它是基于预处理器编译指令#ifndef。

#ifndef FUNC_H
...
#endif

存储持续性、作用域和链接性

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

作用域:描述名称在文件的多大范围内可见。

变量的作用域分为局部和外部。例如函数原型中的参数列表中的变量,他们的作用域就是函数原型的那个参数列表,所以基本来说没什么用,这样一来函数原型中的参数列表中的参数名字无论是什么都无所谓了。

C++使用四种不同的方案来存储数据,这些方案决定来变量保留在内存中的时间(存储持续性)以及程序的哪一部分可以访问它(作用域和链接性)。

1、自动存储持续性

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

在默认情况下,在函数中声明的函数参数和变量的存储持续性为自动,作用域为局部,没有链接性。

当程序开始执行这些变量所属的代码块时,将为其分配内存,当函数结束时,这些变量都将消失。如果一个函数与外部定义了一个同名的变量,那么在这个函数里面的时候,里面的定义是会自动屏蔽掉函数外面的定义的,也就是说,虽然函数内外都有同名的变量,但是实际上他们是两个变量。当执行完这个变量的时候,外部定义的变量会重新的显现出来。

自动变量是存储在栈内的,因为自动变量一般存在于局部,比如一个函数或者一个代码块,每一次的函数调用都会为这个函数在内存的栈中开辟一段内存空间用来存放这个函数内部定义的自动变量。当函数调用完成的时候,栈顶指针被重置为函数被调用前的值,从而释放新变量使用的内存。栈结构中,数据的进出顺序为先进后出,后进先出,这方便了参数的传递。并且在一个函数或者代码块执行完之后,并不是真正的释放了之前局部变量占有的内存,仅仅是把两个栈指针恢复为执行函数之前的样子。他们之前使用的空间将会被下一个函数覆盖使用。

2、静态存储持续性

在函数外定义的变量和关键字static定义的变量的存储持续性都为静态。他们在程序整个运行过程中都存在。C++有3种存储持续性为静态的变量。

和C语言一样,C++也为静态存储持续性提供了3种链接性:外部链接性(可在其他文件种访问)、内部链接性(只能在当前文件中访问)和无链接性(只能在当前函数或代码块种访问)。这3种链接性都在整个程序执行期间存在,与自动变量相比,它们的寿命更长。由于静态变量的数目在程序运行期间是不变的,因此程序不需要使用特殊的装置(如栈)来管理它们。编译器将分配固定的内存块来管理存储所有的静态变量,这些变量在整个程序执行期间一直存在。另外,如果没有显式的初始化静态变量,编译器将把它设置为0。在默认情况下,静态数组和结构将每个元素或成员的所有位都设置为0。

5种变量存储方式

存储描述 持续性 作用域 链接性 如何声明
自动 自动 代码块 可使用auto
寄存器 自动 代码块 在代码块中使用register
静态,无链接 静态 代码块 在代码块中,使用static
静态,外部链接性 静态 文件 外部 在函数外面
静态,内部链接性 静态 文件 内部 在函数外面,使用static

外部链接性:直接在所有的函数外部定义一个普通变量,也就是我们常说的全局变量,这样的变量在一个程序有很多文件的时候,在每一个文件都是可见的。

内部链接性:也是必须在函数外部进行定义,但是要加上关键字static,此时这个变量只是在它所在的为文件是可见的, 在这个程序的其他文件中还是不可见的。

无连接性:要加上static在函数内部进行定义,其实此时这个静态变量只不过是一个生存期比较长的自动变量而已。

静态持续性、外部链接性

C++中有单定义规则,即变量只能定义一次。C++提供两种变量声明。一种是定义声明或简称定义,它给变量分配内存空间;另一种是引用声明或简称声明,它不给变量分配存储空间,因为它引用已有的变量。

引用声明使用关键字 extern,且不进行初始化;否则声明为定义,导致分配存储空间。如果要在多个文件中使用外部变量,只需在一个文件中包含该变量的定义,但在使用该变量的其他所有文件中,都必须使用关键字extern声明它。

double up;//definition, up is 0
extern int blem;//blem defined elsewhere
extern char gr = 'z';//definition because initialized

静态持续性、内部链接性

将static限定符用于作用域为整个文件的变量时,该变量的链接性为内部。如果文件定义来一个静态外部变量,其名称与另一个文件种声明的常规外部变量相同,则在该文件中,静态变量将隐藏常规外部变量。

静态持续性、无链接性

将static限定符用于代码块中定义的变量。该变量只在该代码块种可用,但它在该代码块不处于活动状态时仍然存在。因此在两次函数调用时之间,静态局部变量的值将保持不变。另外,如果初始化静态局部变量,则程序只在启动时进行一次初始化,以后再调用函数时,将不会再次被初始化。

const限定符

在默认情况下全局变量的链接性为外部,但const全局变量的链接性为内部。因为,如果为外部,根据单定义规则,其他文件需要通过extern关键字来提供引用声明,这将出错。这也意味着,每个文件都有自己的一组常来,而不是所有文件共享一组常量。

如果出于某种原因,希望某个常量的链接性为外部,则可以使用extern关键字来覆盖默认的内部链接性。

extern const int states = 50;

函数和链接性

和C语言一样,C++不允许在一个函数种定义另外一个函数,因此所有函数的存储持续性都为静态的,即在整个执行期间都一直存在。在默认情况下,函数的链接性为外部的,即可以在文件间共享。实际上也可以使用extern指出函数在另一个文件中定义,也可以使用static将函数的链接性设置为内部,使之只能在一个文件中使用。

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

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

4、动态存储持续性

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

动态内存由new和delete控制,而不是由作用域何链接性规则控制,因此可以在一个函数种分配动态内存,而在另一个函数种将其释放。与自动内存不同,动态内存不时LIFO,其分配何释放顺序取决于new和delete在何时以何种方式被使用。通常,编译器使用三块独立的内存:一块用于静态变量,一块用于自动变量,一块用于动态存储。

-- EOF --
发表于: 2015-04-20 08:15
标签: C++ 学习笔记