C++ 内存模型

来源:互联网 发布:淘宝客服外包怎么运营 编辑:程序博客网 时间:2024/06/03 21:16

一,C++内存模型

C++使用三种(在C++11中是四种)不同的方案来存储数据,这些方案的区别是数据保留在内存中的时间。

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

(2),静态存储持续性:在函数外面定义的变量和使用关键字static定义的变量的存储持续性为静态的。它们在程序的整个执行的过程中都存在。C++中有三种存储持续性为静态的变量。

(3),线程存储持续性(C++11):当前,多核处理器很常见,这些CPU可同时处理多个任务。这让程序可将计算放在可并行处理的不同的线程中。如果变量是使用关键字thread_local声明的,则其生命周期与所属的线程一样长。

(4),动态存储持续性:使用new运算符动态分配的内存,在整个程序执行期间将一直存在,直到使用delete运算符将其释放掉或是程序执行结束。这种内存的持续性是动态的。


二,不同的存储方式是如何来描述的

不同的C++存储方式是通过存储持续性、作用域和链接来描述的。

(1),存储持续性:数据在内存中保留的时间。

(2),作用域:描述了名称(变量名)在文件的多大范围内可见。例如,在函数中定义的变量可以在该函数中使用不能再函数外面使用,而在函数外面定义的变量可以在所有的函数中使用。

(3),连接性:描述了名称如何在不同文件间共享。连接性为外部的名称可以在文件间共享,连接性为内部的名称只能由一个文件中的函数共享。


三,自动存储持续性(有 2 种存储持续性为自动的变量)

1,自动变量:

在函数中定义的变量或是函数参数的存储持续性为自动的,作用域为局部没有连接性,系统不会对自动的自动变量进行初始化。

int main(){    int a;    cout<<a;    return 0;}
输出结果
4285790Process returned 0 (0x0)   execution time : 0.015 sPress any key to continue

 

2,寄存器变量:

关键字register最初是由C语言引入的,它建议编译器使用CPU寄存器来存储自动变量,目的是为了提高访问变量的速度。

register int a = 10;

四,静态存储持续性(有 3 种存储持续性为静态的变量)

1,连接性为外部的静态存储变量,必须要在函数的外面声明。

2,连接性为内存的静态存储变量,必须要在函数的外面声明,并使用static关键字。

3,没有连接性的静态存储变量,必须要在函数里面声明,并使用static关键字。如果初始化了静态局部变量,则程序只在启动时进行一次初始化,以后再调用函数时,将不会像自动变量那样再次被初始化。

注意:未被初始化的静态变量的值被初始化为0。


五,C++单定义规则

该规则指出,变量只能有一次定义。为了满足这种需求,C++提供了两种变量声明。一种是定义声明,它给变量分配存储空间;另一种是引用声明,它不给变量分配存储空间,因为它引用已有的变量。引用声明使用关键字extern,且不进行初始化,否则,声明为定义,导致分配存储空间。


六,C++对常量类型的规则的修改

1,在默认情况下,全局变量的连接性为外部的,但const全局变量的连接性为内部的。这样的修改让程序员很轻松。

//const.hconst int INF_MAX = 100;const int MIN = 0;

例如,我们创建一个头文件const.h,将一组常量放到这个头文件中,并在同一个程序的多个文件中使用这个头文件。那么,预处理器将头文件的内容包含到每个源文件后,所有的源文件都下面的定义:

const int INF_MAX = 100;const int MIN = 0;
如果全局声明的const变量的连接性像常规变量那样是外部的,则根据单定义规则,这将出错。也就是说只有一个文件可以包含前面的声明,而其他的关键字必须使用extern关键字来提供引用声明。另外,只用未使用extern关键字的声明才能进行初始化。全局定义的const变量的连接性为内部的,意味着每个文件都有自己的一组常量,而不是所有的文件共享一组常量。每个定义都是其所属文件私有的,这就是能把常量定义放在头文件中的原因。


七,函数的连接性
和变量一样函数也有连接性。和C语言一样,C++不允许在一个函数中定义另外一个函数,因此,所有函数的存储连接性为静态的,即在整个程序执行期间一直存在。在默认情况下函数的连接性为外部的,即可以在文件间共享。实际上可以使用extern关键字来指出函数是在另一个文件中定义的。可以使用static关键字将函数的连接性设置为内部的,使之只能在一个文件中使用,必须同时在函数原型与函数声明中使用该关键字:

//函数原型static int display();//函数声明static int display(){        cout<<"hello world."<<endl;}


八,编译器在哪里查找函数
假设在程序的一个文件中调用一个函数,编译器将到哪里去寻找函数的定义呢?如果该文件中的函数原型指出这个函数是静态的,则编译器只在该文件中查找函数定义;否则,编译器在所有的程序文件中查找;如果在程序文件中没有找到,编译器将在库中搜索。


九,动态内存分配
1,使用new动态分配内存

如果要为内置的类型分配空间并初始化,可在类型名称后面加上初始值,并将其用括号括起来。

int *p = new int(8);cout<<*p<<endl;
注意:当内存不足时,new可能会失败,最初的10年C++让new返回空指针,但现在引发异常std::bad_alloc。


2,new运算符与函数替换

运算符new与new[]分别调用以下函数:
void * operator new(std::size_t);  //use by newvoid * operator new[](std::size_t);  //use by new[]
delete与delete[]调用的释放函数
void operator delete(void *);  //use by deletevoid operator delete[](void *);  //use by delete[]


3,定位new运算符

通常,new负责在堆中找出一块满足需求的内存块。new运算符还有一种变体,被称为定位new运算符,它能让你指定要使用的位置。程序员可以使用这种特性来设置其内存管理规则或在特定的位置创建对象。

#include <iostream>using namespace std;char buffer[100];int main(){        int *p = new (buffer)int[20];  //buffer即为要指定的位置        return 0;}
0 0