存储持续性、作用域和链接性
存储持续性、作用域和链接性#
-
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表示的是链接性。
- 有时被称为关键字重载。
- 除了默认的零初始化外,还可以对静态变量进行常量表达式的初始化和动态初始化。
- 零初始化和常量表达式初始化都被称为静态初始化。意味着在编译器处理文件时初始化变量。
- 动态初始化意味着变量将在编译后初始化。
c++11新增了constexpr关键字。这增加了创建常量表达式的方式。#include<iostream> int x;//零初始化 int y = 5;//常量表达式初始化 long z = 13*13;//常量表达式初始化 const double pi = 4.0*atan(1.0);//动态初始化
-
静态持续性和外部链接性
- 链接性为外部的变量通常简称为外部变量。
- 他们的存储持续性为静态。
- 作用域为整个文件。
- 在每个使用外部变量的文件中都必须声明它。
- 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 *。
-