【C++】学习笔记四十五——存储持续性、作用域、链接性

xiaoxiao2021-02-28  84

存储持续性

自动存储持续性:在函数定义中声明的变量,作用域为局部,没有链接性静态存储持续性:在函数外部定义的变量和使用static定义的变量线程存储持续性:使用thread_local声明的变量动态存储持续性:用new分配的内存

静态持续变量

有三种链接性:

外部链接性(可在其他文件中访问):不在任何函数内声明内部链接性(只能在当前文件中访问):不在任何函数内,用static声明无链接性(只能在当前函数或代码块中访问):在代码块中,用static声明

静态持续变量默认为0。

变量只能有一次定义(单定义规则),但在每个使用外部变量的文件中都必须声明它。因此有两种变量声明:定义声明和引用声明。 引用声明使用extern,且不进行初始化;否则,声明为定义,会分配内存空间。

单定义规则并非意味着不能有多个变量的名称相同,例如,在不同函数中声明的同名变量是彼此独立的,他们都有自己的地址。

如果在函数中生命了一个与外部变量同名的变量,将被视为一个自动变量的定义。

程序9.5

#include <iostream> using namespace std; //external variable double warming = 0.3; void update(double dt); void local(); int main() { cout << "Global warming is " << warming << " degrees.\n"; update(0.1); cout << "Global warming is " << warming << " degrees.\n"; local(); cout << "Global warming is " << warming << " degrees.\n"; system("pause"); return 0; }

程序9.6

#include <iostream> extern double warming; void update(double dt); void local(); using std::cout; void update(double dt) { extern double warming; warming += dt; cout << "Updating global warming to " << warming; cout << " degrees.\n"; } void local() { double warming = 0.8; cout << "Local warming " << warming << " degrees.\n"; cout << "But global warming = " << ::warming; cout << " degrees."; }

程序9.5和9.6的出输出表明,main()和update()都可访问warming,update()修改了warming。 local()函数表明定义与全局变量同名的局部变量后,局部变量将隐藏全局变量。

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

如果要在其他文件中使用相同的名称来表示其他变量,只省略extern是不够的,还需要将一个文件中的变量定义为静态外部变量(static)。

在多文件程序中,可以在一个文件(且只能在一个文件)中定义一个外部变量,使用该变量的其他文件必须使用关键字extern声明。

无链接性的局部变量:将static限定符用于代码块中定义的变量。这将导致局部变量的存储持续性为静态的,虽然该变量只在代码块中可用,但他在该代码块不处于活动状态时仍然存在。因此在两次函数调用之间,静态局部变量的值将保持不变。

程序9.9

#include <iostream> const int ArSize = 10; void strcount(const char * str); int main() { using namespace std; char input[ArSize]; char next; cout << "Enter a line:\n"; cin.get(input, ArSize); while (cin) { cin.get(next); while (next != '\n') cin.get(next); strcount(input); cout << "Enter next line (empty line to quit):\n"; cin.get(input, ArSize); } cout << "Bye\n"; system("pause"); return 0; } void strcount(const char * str) { using namespace std; static int total = 0; //static local variable int count = 0; cout << "\"" << str << "\" contains "; while (*str++) count++; total += count; cout << count << " characters\n"; cout << total << " characters total\n"; }

该程序演示了一种处理行输入可能长于目标数组的方法。方法cin.get(input,ArSize)将一直读取输入,直到到达行尾或读取了ArSize-1个字符为止。它把换行符留在输入队列中。该程序使用cin.get(next)读取行输入后的字符。如果next是换行符,表明cin.get(input,ArSize)读取了整行;否则说明还有字符没有被读取。随后利用一个循环来丢弃余下的字符。另外,该程序说明用get(char *,int)读取空行将导致cin为false。

每次函数被调用时,自动变量count都被重置为0;然而静态变量total只在程序运行时被设置为0,之后每次调用,其值都保持不变。

说明符和限定符

存储说明符:

auto(在C++11中不再是说明符,而用于自动类型推断)register(在声明中指示寄存器存储)staticexternthread_local(C++11新增的)mutable

cv-限定符:

constvolatile

volatile作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。简单地说就是防止编译器对代码进行优化。

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

struct data { char name[30]; mutable int accesses; ... }; const data veep = {"Claybourne Clodde", 0, ...}; strcpy(veep.name,"Joye Joux"}; //不允许 veep.accesses++; //允许

在C++中,const对默认存储类型有影响:在默认情况下全局变量的链接性为外部的,但const全局变量的链接性为内部的。就像使用了static一样。 如果出于某种原因,希望某个常量的链接性为外部的,则可以使用extern来覆盖默认的内部链接性:

extern const int states = 50;

函数的链接性

函数也有链接性,默认的链接性是外部的,即可以在文件件共享。实际上,可以在函数原型中使用extern来指出函数实在另一个文件中定义的,不过这是可选的。

还可以使用static将函数的链接性设置为内部的,使之只能在一个文件中使用,必须同时在原型和定义中使用static。

不允许在一个函数中定义另一个函数,因此所有函数的存储持续性都自动为静态的,即在整个程序执行期间都一直存在。

动态分配

使用new来分配动态内存,使用delete来释放内存,而不由作用域和链接性规则控制。因此,可以在一个函数中分配动态内存,而在另一个函数中将其释放。 使用new初始化 C++98:

int *pi = new int (6); //*pi为6 double *pd = new double (99.99); // *pd为99.99

C++11: 列表初始化

struct where {double x; double y;double z;}; where * one =new where {2.5, 5.3, 7.2}; int * ar = new int [4] {2, 4, 6, 7}; int *pin = new int {6}; double *pdo = new double {99.99};

“定位new”(placement new)运算符 通常new在堆(heap)中寻找一个足以满足要求的内存块。 定位new运算符可以指定要使用的位置,使用定位new,需要包含头文件new。使用定位new时,变量后边可以有方括号,也可以没有。

#include <new> struct chaff { char dross[20]; int slag; }; char buffer1[50]; char buffer2[500]; int main() { chaff *p1, *p2; int *p3, *p4; //常规new p1 = new chaff; p3 = new int [20]; //定位new p2 = new (buffer1) chaff; //将结构放置于buffer1 p4 = new (buffer2) int [20]; //将int 数组放置于buffer2 ... }

程序9.10

#include <iostream> #include <new> const int BUF = 512; const int N = 5; char buffer[BUF]; int main() { using namespace std; double *pd1, *pd2; int i; cout << "Calling new and placement new:\n"; pd1 = new double[N]; pd2 = new (buffer) double[N]; for (i = 0; i < N; i++) pd2[i] = pd1[i] = 1000 + 20.0*i; cout << "Memory addresses:\n" << " heap: " << pd1 << " static: " << (void *)buffer << endl; cout << "Memory contents:\n"; for (i = 0; i < N; i++) { cout << pd1[i] << " at " << &pd1[i] << ";"; cout << pd2[i] << " at " << &pd2[i] << endl; } cout << "\nCalling new and placement new a second time:\n"; double *pd3, *pd4; pd3 = new double[N]; //find new address pd4 = new (buffer) double[N]; //overwrite old data for (i = 0; i < N; i++) pd4[i] = pd3[i] = 1000 + 40.0*i; cout << "Memory contents:\n"; for (i = 0; i < N; i++) { cout << pd3[i] << " at " << &pd3[i] << ";"; cout << pd4[i] << " at " << &pd4[i] << endl; } cout << "\nCalling new and placement new a third time:\n"; delete[] pd1; pd1 = new double[N]; pd2 = new (buffer + N * sizeof(double)) double[N]; for (i = 0; i < N; i++) pd2[i] = pd1[i] = 1000 + 60.0*i; cout << "Memory contents:\n"; for (i = 0; i < N; i++) { cout << pd1[i] << " at " << &pd1[i] << ";"; cout << pd2[i] << " at " << &pd2[i] << endl; } delete[] pd1; delete[] pd3; system("pause"); return 0; }

该程序没有使用delete来释放定位new运算符分配的内存,事实上,这个例子不能这样做。因为buffer指定的内存是静态内存,而delete只能用于指向常规new运算符分配的堆内存。也就是说,buffer位于delete的管辖范围之外。 如果buffer是使用常规new创建的,则可以使用delete。

转载请注明原文地址: https://www.6miu.com/read-81209.html

最新回复(0)