引入名称空间之前C++变量和函数的存储方案及特点

来源:互联网 发布:ubuntu ports 源 编辑:程序博客网 时间:2024/06/14 10:20

变量

变量存储方案

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

自动存储持续性:

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

静态存储持续性:

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

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

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

动态存储持续性:

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

作用域

作用域(scope)描述了名称在文件(翻译单元)的多大范围内可见。

C++的作用域有多种:局部作用域、全局作用域(也叫文件作用域)、名称空间作用域(全局作用域是其特例)、函数原型作用域。

链接性

链接性(linkage)描述了名称如何在不同单元内共享。

C++变量的链接性有3种:无链接性、内部链接性、外部链接性。



单定义规则

C++的单定义规则指出,变量只能有一次定义。为满足这种需求,C++提供了两种变量声明。一种是定义声明,它给变量分配存储空间;另一种是引用声明,它不给变量分配存储空间,因为它引用已有的变量。

单定义规则并非意味着不能有多个变量的名称相同。例如在不同函数中的同名变量是彼此独立的,它们都有各自的地址,所以虽然它们同名但不是同一个变量。另外,局部变量可能隐藏同名的全局变量。

引用声明使用关键字extern,且不进行初始化,否则该声明为定义声明,导致分配存储空间。

//1.cpp#include <iostream>int n = 8;void demo (void){    using std::cout;    using std::endl;    cout << "在1.cpp中:" << endl;    cout << "n = " << n << endl << "n is at: " << &n << endl;}

//main.cpp#include <iostream>#include "1.cpp"extern int n;int main (void){    using std::cout;    using std::endl;    demo();    cout << "在main.cpp中:" << endl;    cout << "n = " << n << endl << "n is at: " << &n << endl;    return 0;}



自动变量

在默认情况下,在函数中声明的函数参数和变量的存储持续性为自动,作用域为局部,无链接性。也就是说,在两个函数中声明了名称相同的两个变量,则创建了两个独立的变量——只有在定义它们的函数中才能使用它们。另外,当程序开始执行这些变量所属的代码块时,将其分配内存:当函数结束时,这些变量都将消失(执行到代码块时,将为变量分配内存,但其作用域的起点为其声明位置)。如果在代码块中定义了变量,则该变量的存在时间和作用域将被限制在该代码块内。

下面的程序说明了自动变量的两种特性。如果试图执行注释掉的代码,编译器将会报错。

#include <iostream>using namespace std;void ShowA (void){    int a = 3;    cout << "ShowA函数中:" << endl;    cout << "a = " << a << endl << "a is at: " << &a << endl;}int main (void){    ShowA();    cout << "在main函数中:" << endl;    int a = 33;    {        int b = 24;        cout << "代码块内:" << endl;        cout << "a = " << a << endl << "a is at: " << &a << endl;        cout << "b = " << b << endl << "b is at: " << &b << endl;    }    cout << "代码块外:" << endl;    cout << "a = " << a << endl << "a is at: " << &a << endl;    //cout << "b = " << b << endl << "b is at: " << &b << endl;    return 0;}


由于自动变量的数目随函数的开始和结束而增减,因此程序使用特殊的管理方法对其进行管理。常用方法是留出一段内存存放自动变量,并将这段内存视为栈,以管理变量的增减。当函数被调用时,其自动变量将被加入到栈中,函数调用结束,其自动变量也随之弹出


寄存器变量

寄存器变量顾名思义就是存放在寄存器中的变量,用register关键字声明,并且只有自动变量和函数参数才能声明为寄存器变量。它提示编译器该变量使用得很多,建议编译器使用CPU寄存器来存储自动变量。

如果某些自动变量在函数中需要频繁使用,需要多次访问这些变量的内存单元,那么将其声明为寄存器变量后,编译器会将其放入寄存器中,以省去访问内存单元的时间,提高运行效率。

对于寄存器变量不能用取地址符,且只限于整型和指针类型数据。

寄存器变量的数目随机器而定,如果声明多了也只是对前几个有效。在C++11中这种提示、建议作用也失去了,关键字register只是显式地指出变量是自动的。


静态变量

静态变量有3种链接性:外部链接性(可在其他文件中访问)、内部链接性(只能在当前文件中访问)和无链接性(只能在当前函数或代码块中访问)。无论何种链接性,静态变量堵在整个程序执行期间存在(链接性自然也随之存在),与自动变量相比,它们的寿命更长。由于静态变量的数目在程序运行期间是不变的,因此程序不需要使用特殊的装置(如栈)来管理它们。编译器将分配固定的内存块来存储所有的静态变量。另外,如果没有显式地初始化静态变量,编译器将把它设置为0(包括静态数组中的元素和静态结构中的结构成员)。


静态外部变量

链接性为外部的静态变量称为外部变量,其作用域为整个文件。外部变量也称为全局变量(相对于局部的自动变量)。如果要在多个文件中使用外部变量,只需在一个文件中包含该变量的定义(单定义规则),但在使用该变量的其他文件中,都必须使用关键字extern声明它。

静态内部变量

static限定符用于作用域为整个文件的变量时,该变量的链接性为内部的。

另外,如果使用const限定符定义外部变量,那么该变量的链接性为内部,就像使用了static限定符一样。如果想要将常量恢复外部链接性,可以使用extern关键字来覆盖默认的内部链接性。在这种情况下,必须在所有使用该变量的文件中使用extern关键字来声明它,这与常规外部变量不同。定义常规外部变量时,不必使用extern关键字,但在其他使用该变量的文件中,必须使用引用声明extern关键字。


静态局部变量

静态变量中无链接性的变量称为静态局部变量,该变量在代码块中用static限定符声明。静态局部变量只在指定代码块中可用,但它在该代码块不处于活动状态时仍然存在。如果初始化了静态局部变量,该程序只在启动时进行一次初始化,以后再调用函数时,将不会像自动变量一样再次被初始化,因此在两次函数调用之间,静态局部变量的值将保持不变。


函数

函数与变量一样也有链接性,但可选范围较小。C++不允许在一个函数中定义另外一个函数,因此所有函数的存储持续性都自动为静态的,即在整个程序执行过程中一直存在。在默认情况下,函数的链接性为外部,可以在文件间共享。实际上,可以在函数原型中使用关键字extern来指出函数是在另一个文件中定义的,不过这是可选的。还可以使用关键字static将函数的链接性设置为内部的,使之只能在一个文件中使用,但必须在原型和定义中都使用该关键字。

单定义规则对绝大部分函数都适用,只有内联函数不受这项规则的约束,但C++要求每个使用同一内联函数的文件都有内联函数的定义且定义必须相同。


语言链接性

另一种形式的链接性--称为语言链接性(language linking)也对函数有影响。首先介绍一些背景知识。链接程序要求每个不同的函数都有不同的符号名。在C语言中,一个名称只对应一个函数,因此这很容易实现。为满足内部需要,C语言编译器可能将spiff这样的函数名翻译为_spiff。这种方法被称为C语言链接性(C language linkage)。但在C++中,同一个名称可能对应多个函数,必须将这些函数翻译为不同的符号名称。因此,C++编译器执行名称矫正或名称修饰(参见第8章),为重载函数生成不同的符号名称。例如,可能将spiffint)转换为_spoff_i,而将spiffdoubledouble)转换为_spiff_d_d。这种方法被称为C++语言链接(C++ language linkage)。

C++编译器默认函数的语言链接性为C++。但我们可以在函数原型中显式地指出要使用哪一种语言链接性:

extern "C" void spiff (int);

extern void spiff (int);//默认为C++链接性

extern "C++" void spiff (int);

CC++链接性是C++标准指定的说明符,但实现可提供其他语言链接性说明符。


关键字volatile和mutable


volatile

关键字volatile表明,即使程序代码没有对内存单元进行修改,其值也可能发生变化。例如,可以将一个指针指向某个硬件位置,其中包含了来自串行端口的时间或信息。在这种情况下,硬件(而不是程序)可能修改其中的内容。或者两个程序可能互相影响,共享数据。该关键字的作用是为了改善编译器的优化能力。例如,假设编译器发现,程序在几条语句中两次使用了某个变量的值,则编译器可能不是让程序查找这个值两次,而是将这个值缓存到寄存器中,这是编译器对程序的一种优化。如果不将变量声明为volatile,则编译器将进行这种优化;将变量声明为volatile的作用相当于告诉编译器不要进行这种优化。

mutable

mutable关键字用来指出,即使结构(或类)变量为const,其某个成员也可以被修改。

#include <iostream>#include <string>struct A{    int A_num;    mutable std::string A_name;};int main (void){    const A a = {33,"AAA"};    //a.A_num = 1;    a.A_name = "AA";    std::cout << a.A_name << std::endl;    return 0;}

结构体A被声明为const,则其成员不可被修改(如注释掉的代码),但声明为mutable的成员不受约束。



本文大部分内容摘自《C++ Primer Plus(第6版)中文版》第9章

















原创粉丝点击