C++内存模型

程序开发时常使用多个文件。一般组织策略是,使用头文件定义数据类型,以及提供操纵这些数据类型的函数原型;将函数定义放在一个独立的源代码文件中;将主函数和使用操纵这些数据类型的函数放在其它的文件中。然而,将一个程序放在多个文件中会引出一些新的问题,即信息在多文件中是怎样共享的呢?这涉及到三个概念存储持续性、作用域和链接性。

       存储持续性描述了名称在文件中持续的时间。

       作用域描述了名称在文件中的可见范围。

       链接性描述了名称如何在不同单元间共享。链接性为外部的名称可在文件间共享,链接性为内部的名称只能在所属的文件中共享。自动变量没有链接性,因为它们不能共享。

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

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

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

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

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

4、动态存储持续性:用new运算符分配的内存将一直存在,直到使用delete运算符将其释放或程序结束为止。这种内存的存储持续性为动态。


1.1自动存储持续性变量


在默认情况下,在函数中声明的函数参数和变量的存储持续性为自动,作用域为局部,没有链接性。可以使用任何在声明时其值为已知的表达式来初始化自动变量。自动变量存储在栈中。自动变量的存储方式见下表:

表:自动变量的存储方式

存储描述

持续性

作用域

链接性

如何声明

 

自动

 

 

自动

 

代码块

 

在代码块中

或使用关键字auto,C++11之后使用register

寄存器

自动

代码块

在代码块中,使用关键字register

测试如下:
#include <iostream>

using std::cout;
using std::endl;

void autoVariableTest(int x);

int main()
{
    int test = 7;
    cout << "test = " << test << " The address of main-test is "
         << &test << endl;
    autoVariableTest(test);
    cout << "test = " << test << " The address of main-test is "
         << &test << endl;

    return 0;
}

void autoVariableTest(int x)
{
    int test = x;
    cout << "test = " << test << " The address of auto-f-test is "
         << &test << endl;
    cout << "x = " << x << " The address of x is " << &x << endl;
    {
        int test = 6;
        cout << "test = " << test << "The address of auto-f-block-test is "
             << &test <<endl;

    }
    test = 8;
    cout << "test = " << test << " The address of auto-f-test is "
         << &test << endl;
}



1.2静态存储持续性变量

和C语言一样,C++为静态存储持续性变量提供了3种链接性:外部链接性,可在其它文件中访问;内部链接性,只能在当前文件中访问;无链接性,只能在当前函数或代码块中访问。这3中链接性都在整个程序执行期间存在,与自动变量相比,它们的持续时间更长。编译器一般分配固定的静态内存来存储所有的静态变量,如果没有显示初始化静态变量,编译器将它设置为0,但自己进行变量初始化是一个良好的编程习惯。静态变量的存储方式见下表:

表:静态变量存储方式

存储描述

持续性

作用域

链接性

如何声明

静态,无链接性

静态

代码块

在代码块中,使用关键字static

静态,内部链接性

静态

文件

内部

不在任何函数内,代码块中,使用关键字static

静态,外部链接性

静态

文件

外部

不在任何函数内和代码块中

 


测试如下:

file1.cpp

#include <iostream>

using std::cout;
using std::endl;

double add(double x1, double x2);

double test_file1 = 0; //静态变量,外部链接性
double test_file2 = 0; //测试文件2中的内部链接性变量test_file2是否会改变此值
extern double test_file3; //测试与文件2中的外部链接性变量test_file3是否冲突,发现是有冲突的,需加extern

int main(void)
{
    int x = 1;
    int y = 2;
    cout << "x = " << x << " y = " << y << endl;
    cout << "test_file1 = " << test_file1 << endl;
    cout << "test_file2 = " << test_file2 << endl;
    cout << "test_file3 = " << test_file3 << endl;
    test_file3 = add(x, y);
    test_file3 += add(x, y);
    test_file3 += add(x, y);
    cout << "test_file3 = " << test_file3 << endl;
    cout << "test_file1 = " << test_file1 << endl;
    cout << "test_file2 = " << test_file2 << endl;
    cout << "test_file3 = " << test_file3 << endl;

    return 0;

}
file2.cpp

#include <iostream>

extern double test_file1; //test_file1来自于其他文件
static double test_file2; //静态变量,内部链接性
double test_file3 = 0; //静态变量,外部链接性

double add(double x1, double x2)
{
    static int count = 0; //静态变量,无链接性
    double y = x1 + x2;
    test_file1 = y;
    test_file2 = y;
    test_file3 += y;
    count++;
    std::cout << "-------add------" << std::endl;
    std::cout << "test_file1 = " << test_file1 << std::endl;
    std::cout << "test_file2 = " << test_file2 << std::endl;
    std::cout << "test_file3 = " << test_file3 << std::endl;
    std::cout << "count = " << count << std::endl;
    std::cout << "-------add------" << std::endl;
    return y;
}

需要注意一下几点:

1、         在每个使用外部变量的文件中,都必须声明它;此外,需遵循单定义规则,即变量只能定义一次。

2、         如果要在多个文件中使用外部变量,只需在一个文件中包含该变量的定义(单定义规则),但在使用该变量的其他文件中,都必须使用关键字extern声明它。

3、         C++提供了作用域解析符::,放在变量名称前面表示使用该变量的全局版本。

4、         初始化了静态局部变量,则程序只在启动时进行一次初始化。以后再调用函数时,将不会像自动变量那样再次被初始化。

5、         const全局变量默认链接性为内部的。如果需要将const全局变量设置为外部链接性,则在每个文件中都必须在定义或声明之前加上extern,如 extern constint flag = 2;


1.3       语言链接性

链接程序要求每个不同的函数都有不同的符号名。在C语言中,一个名称只对应一个函数,如doubleadd(double, double)翻译为_add, 这种方法称为C语言链接性。而在C++中,由于重载等语言特性,一个名称可能对应多个函数,因此不需将这些函数翻译为不同的符号名称,所以C++编译器执行名称矫正或名称修饰,为重载函数生成不同的符号名称,如doubleadd(double, double)翻译为_add_d_d, double add(int,int)翻译为_add_i,这种方法称为C++语言链接性。可以使用C++标准制定的链接性说明符解决这一问题。

如:

 extern “C” double add(int ,int); //按C原型查找

 extern “C++” double add(int ,int); //按C++原型查找

 extern double add(int ,int); //按C++原型查找

 

1.4         动态分配

1、在分配动态内存时,可以使用new运算符进行初始化,如:

int* pi = newint(6);

int* pi = newint[4] {2, 3, 4, 5}; //C++11

2、运算符new,new[], delete,delete[]调用一下形式的函数,并可被替换与重载。亦即自己可以对其进行定制。注意:delete只能用于释放指向常规new运算符分配的内存。

void* operator new(std::size_t);

void* operator new[](std::size_t);

void* operator delete(std::size_t);

void* operator delete[](std::size_t);

3、定位new运算符

定位new运算符可以在指定的位置上分配内存,使用定位new运算符需要包含头文件new,注意不能使用delete来释放定位new运算符分配的内存。定位new运算符可被重载,但不能被替换。

如: void* operatornew(std::size_t, void*);






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值