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

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

  • c++中使用3中不同的方案存储数据

  • c++11中使用4中不同的方案存储数据。

  • 这些方案的区别在于数据保留在内存中的时间。

  • 自动存储持续性

    • 在函数定义中声明的变量的存储持续性为自动的。2种
  • 静态存储持续性

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

    • 多核处理器中很常见。如果变量是使用thread_local声明的,则其生命周期和所属线程一样长。
  • 动态存储持续性

    • 使用new运算符分配的内存将一直存在,直到使用delete运算符将其释放或程序终止为止。有时被称为自由存储或堆。
  • 作用域和链接

    • 作用域描述了名称在文件的多大范围可见。
      • 作用域为局部的变量只能在定义它的代码块中用。
      • 作用域为全局的变量在定义位置到文件结尾之间都可用。
      • 自动变量为局部作用域。静态变量为全局作用域还是局部作用域取决于它是如何被定义的。
      • 在函数原型作用域中使用的名称只在包含参数列表的括号内使用。
      • 在类声明的成员的作用域为整个类。
      • 在名称空间中的声明的变量的作用域为整个名称空间。全局作用域是名称空间作用域的特例。
      • c++函数的作用域为整个类或整个名称空间。但不能是局部。因为不能在代码块中定义函数。如果函数作用域为局部,则只对之间可见。不能被其他函数调用。
    • 链接性描述了名称如何在不同单元间共享
      • 链接性为外部的名称可以在文件间共享。
      • 链接性为内部的名称只能由一个文件中的函数共享。
      • 自动变量的名称没有链接性,因为他们不能共享。
  • 默认情况下,在函数中声明的函数参数和变量的存储持续性为自动,作用域为局部,没有链接性。

  • c++11中的auto用于自动类型推断。但在c语言和以前的c++中auto用于显式的指出变量为自动存储。

  • 寄存器变量

    • register最初是c语言引入的,它建议编译器使用cpu寄存器来存储自动变量。
    • c++11后register只是显式的指出变量是自动的。
  • 静态持续变量

    • c++为静态持续变量提供了3种链接性:外链接性,内链接性,无链接性。
    • 这3种链接性在整个程序执行期间存在。与自动变量相比,寿命更长。
    • 静态变量的数目在程序运行期间不变。所以不使用栈存放。编译器将分配固定的内存块来存储所有的静态变量。
    • 如果没有显式的初始化静态变量,编译器将把该变量设置为0。
    • 静态数组和结构的每个元素或成员的所有位都被设置为0。
    int g = 1000;//外链接性
    static int a = 50;//内链接性
    void func(int n)
    {
        static int count = 0;//无链接性
    }
    
    • 所有的静态变量都有如下初始化特征
      • 未被初始化的静态变量的所有位都被设置为0。
      • 这种变量被称为零初始化的。
    • static的两种含义
      • 在代码块中使用static表示变量是无链接性的静态变量时,static表示的是存储持续性。
      • 在函数外使用static表示内部链接性的静态变量时,static表示的是链接性。
      • 有时被称为关键字重载。
    • 除了默认的零初始化外,还可以对静态变量进行常量表达式的初始化和动态初始化。
      • 零初始化和常量表达式初始化都被称为静态初始化。意味着在编译器处理文件时初始化变量。
      • 动态初始化意味着变量将在编译后初始化。
      #include<iostream>
      int x;//零初始化
      int y = 5;//常量表达式初始化
      long z = 13*13;//常量表达式初始化
      const double pi = 4.0*atan(1.0);//动态初始化
      
      c++11新增了constexpr关键字。这增加了创建常量表达式的方式。
  • 静态持续性和外部链接性

    • 链接性为外部的变量通常简称为外部变量。
    • 他们的存储持续性为静态。
    • 作用域为整个文件。
    • 在每个使用外部变量的文件中都必须声明它。
    • c++有"单定义规则"该规则指出,变量只能有一次定义。
    • c++提供了两种变量声明。
      • 一种是定义声明或简称定义。它给变量分配存储空间。
      • 另一种是引用声明或简称声明。它不给变量分配存储空间,因为它引用已有变量。
      • 引用声明使用关键字extern 且不进行初始化。否则声明为定义,导致分配存储空间。
      double d;//定义声明,分配空间
      extern int i;//引用声明,不分配空间
      extern char c  = 'a';//定义声明,分配空间
      
      • 如果在多个文件中使用外部变量,只需在一个文件中包含该变量的定义。但在使用该变量的其他文件中都必须使用关键字extern声明它。
      // file1.cpp
      //定义变量
      extern int cats = 20;
      int dogs = 22;
      int fleas;
      
      // file2.cpp
      //在其他文件中使用extern声明
      extern int cats;
      extern int dogs;
      
      // file3.cpp
      //在其他文件中使用extern声明
      extern int cats;
      extern int dogs;
      extern int fleas;
      
      
      • 使用作用域解析运算符(::)放在变量前面时,该运算符使用变量的全局版本。从清晰和避免错误的角度来说使用 (::变量) 是更好的选择。
    • 外部存储尤其适用于表示常量数据。因为这样可以使用const来防止数据被修改。
    const char * const months[12] = 
    {
        //...
    }
    
  • 静态持续性和内部链接性

    • 将static限定符用于作用域为整个文件的变量时,该变量的链接性为内部的。在多文件程序中,外部链接性和内部链接性很有差别。
    //file1
    int errors = 20;
    //file2 
    int errors = 10;
    //该代码将导致失败,因为违反了单定义规则。
    //file1
    int errors = 20;
    //file2 
    static int errors = 10;
    //该代码将成功,内部变量将隐藏常规外部变量
    
  • 静态持续性和无链接性

    • 这意味着该变量只在该代码块中可用。但他在该代码块不处于活动状态时仍然存在
    • 因此在两次函数调用之间,静态局部变量的值将保持不变。
    • 如果初始化了静态局部变量,则程序只在启动时进行一次初始化。以后再调用函数时,将不会像自动变量那样再次被初始化。
  • cv限定符

    • volatile表明,即使程序代码没有对内存单元进行修改,其值也可能发生变化。改善优化能力。
  • mutable

    • 可以用它来指出即使结构或类变量为const,其某个成员也可以被修改。
    struct data
    {
        char name[10];
        mutable int accesses;
    }
    
  • const

    • 在c++中const限定符对默认存储类型稍有影响。在默认情况下,全局变量的链接性为外部。但const全局变量的链接性为内部。也就是说在c++看来,全局const定义就像static说明符一样。
    • 内部链接性还意味着每个文件都有之间的一组常量。而不是所有文件共享一组常量。每个定义都是其所属文件私有的。
    • 如果希望某个常量的链接性为外部的,则可以使用extern关键字来覆盖默认的内部链接性。extern const int a = 20;
    • 这种情况下,在其他文件中使用该变量时需要使用extern关键字来引用声明。与常规外部变量不同。
  • 函数和链接性

    • c++不允许在一个函数中定义另一个函数。所有函数的存储持续性都自动为静态。
    • 在默认情况下,函数的链接性为外部的。
    • 实际上可以使用extern来指出函数是在另一个文件中定义的。
    • 还可以使用static将函数的链接性设置为内部的。必须同时在原型和定义中使用该关键字。
  • 语言链接性

    • 语言链接性对函数也有影响。
    • 链接程序要求每个不同的函数都有不同的符号名。
    • 例如spiff(int)转换为_spiff_i,spiff(double, double)转换为_spiff_d_d。这种方法被称为c++语言链接。
    • 我们可以使用函数原型来指定要使用的约定。
    • extern "C" void spiff(int); 使用c语言链接性。
    • extern void spiff(int); 使用c++语言链接性。
    • extern "C++" void spiff(int); 使用c++语言链接性。
  • 使用new运算符初始化

    • 为内置的标量类型分配存储空间并初始化。
    • int *p = new int(6);
    • 要初始化常规结构或数组需要使用大括号的列表初始化。这要求编译器支持c++11.
    • struct where {double x; double y; double z};
    • where * one = new where{2.5, 2.4, 2.3};
    • int * a = new int[4] {2,3,5,6};
    • 在c++11中还可以将列表初始化由于单值变量。
    • int * p = new int {6};
  • new 失败时

    • new可能找不到请求的内存量。
    • 以前c++在这种情况下让new返回空指针。
    • 现在将引发异常std::bad_alloc
  • new 运算符,函数和替换函数

    • 运算符new和new[]分别调用分配函数
    • void * operator new(std::size_t);
    • void * operator new [] (std::size_t);
    • 同样也有释放函数
    • void operator delete(void *);
    • void operator delete[](void *);
    • 这些函数可以定制。
  • 定位new运算符

    • include< new >

    • 将使用指定的位置为new提供空间
    • char buffer[20];
    • int *p = new (buffer) int;
    • 不可以使用delete释放。
    • delete只用于指向常规new运算符分配的堆内存。
    • 如果buffer是使用new创建的,那么可以使用delete释放。
    • 另一种用法是,将其与初始化结合使用。从而将信息放在特定的硬件地址。
    • 定位运算符的工作原理。返回传递给它的地址,并且强制转换为void *。
posted @ 2019-12-02 23:13  yangzixiongh  阅读(309)  评论(0编辑  收藏  举报