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

1.c++ pp page 250

存储持续性:数据保留在内存中的时间;(注:自动存储持续性:在函数定义中声明的变量(包括函数参数)的存储持续性为自动的。它们在程序开始执行其所属的函数或代码块时被创建,而不是程序运行到定义语句时才被创建)

作用域描述了名称在文件(翻译单元)的多大范围内可见。全局作用域(文件作用域)的变量在定义位置到文件结尾之间都可用。自动变量的作用域为局部。静态变量的作用域是全局还是局部取决于它是被如何定义的。在名称空间中声明的变量的作用域为整个名称空间

链接性描述了名称如何在不同单元间共享,链接性分为外部(可在文件间共享)、内部(可在整个文件内共享)和没有链接性(自动变量无链接性,不能共享)。

 

2.自动存储持续性

1)默认情况下,函数中声明的函数参数和变量的存储持续性为自动,作用域为局部,没有链接性。当程序开始执行这些变量所属的代码块时,将为其分配内存;当函数结束时,这些变量将消失。(注意,执行到代码块时,将为变量分配内存,但其作用域的起点为其声明位置

2)自动变量和栈 p252

程序在运行时对自动变量进行管理;常用的方法是留出一段内存,并将其视为栈。

在函数传递参数时也使用栈。p252-253

 

3.静态持续变量 p253

1)c++为静态存储持续性变量提供了3中链接性:外部链接性(可在其他文件中访问)、内部链接性(只能在当前文件中访问)和无链接性(只能在当前函数或代码块中访问)。

编译器将分配固定的内存块来存储所有的静态变量,这些变量在整个程序执行期间一直存在

...
int global = 1000;//静态存储持续性,外部链接性
static int one_file = 50;//静态存储持续性,内部链接性
int main()
{
...
}
void funct1(int n)
{
static int count = 0;静态存储持续性,无链接性
int llama = 0;
...
}

上述代码中,count 与 自动变量llama不同,在funct1()函数没有执行时,lemma是自动变量,自然不在内存中;但count不管funct1()是否执行,其都在内存中。(程序执行期间一直存在)\

2)静态变量的初始化分为零初始化,常量表达式初始化和动态初始化。

零初始化指变量被设置为0;所有的静态变量刚开始都被零初始化,不管程序员是否显示的初始化了它。

如果使用常量表达式初始化了变量,且编译器仅根据文件内容(包括被包含的头文件)就可计算表达式,编译器将执行常量表达式初始化。

零初始化和常量表达式初始化被统称为静态初始化。静态初始化意味着编译器在处理文件(翻译单元)时初始化变量

如果没有足够的信息,变量将被动态初始化。动态初始化意味着变量将在编译后初始化

 

#include <cmath>
int x;//零初始化
int y = 5;//常量表达式初始化
long z = 13*13;//常量表达式初始化
const double pi = 4.0 * atan(1.0);//要调用函数atan(),这需要等到该函数被链接且程序执行时,因此是动态初始化。                                           

 静态变量即静态存储持续性变量。

 

4.静态存储持续性、外部链接性 p254

链接性为外部的变量简称为外部变量,它们的存储持续性为静态,作用域为整个文件(外部变量的作用域也是整个文件)。外部变量也被称为全局变量

1)单定义规则

c++提供了两种变量声明:定义声明(或简称为“定义”),它给变量分配存储空间。另一种是引用声明(简称为“声明”),它不给变量分配存储空间。

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

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

//file1.cpp
int cats = 20;//定义声明
...
//file2.cpp
extern int cats;//引用声明

注意,单定义规则是针对外部变量的声明的。

2)如果在函数中声明了一个与外部变量同名的变量,局部变量将隐藏全局变量。

如果要使用该全局变量,应在该全局变量之前加上标准名称空间std中的作用域解析运算符(::)

//file.cpp
int cats = 20;
...
//file2.cpp
extern int cats;
int main()
{
    int cats = 10;
    cout<<cats<<endl;//output 10
    cout<<std::cats<<endl;//output 20
    return 0;
}

 

5.静态存储持续性,内部链接性 p257

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

如果文件定义了一个链接性为内部的静态存储持续性的变量,其名称与另一个文件中声明的常规外部变量相同,则在该文件中,链接性为内部的静态存储持续性的变量将隐藏常规外部变量。

//file1.cpp
int errors = 20;
...
//file2.cpp
static int errors = 5;
void froobish()
{
    cout<<errors;//output 5
...
}

上述代码中,file2.cpp中的链接性为内部的静态存储持续性变量屏蔽file1.cpp中的常规外部变量。

且上述代码未违反单定义规则,因为单定义规则是指常规外部变量只能在多个文件中有一个定义,但file2.cpp中的error的链接性未内部,并非要提供外部定义。

 

6.静态存储持续性,无链接性--局部变量 p258

在代码块中使用static时,将导致局部变量的存储持续性为静态的。该变量虽然只在该代码块中可用,但该变量在整个程序执行期间一直存在,即当没有运行该代码块时,该变量仍然存在于内存中。

因此在两次函数调用之间,静态局部变量的值保持不变。

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

 

7.说明符和限定符 p260

存储说明符 auto register static extern thread_local mutable

cv-限定符:const volatile

const:(p260)

默认情况下全局变量(外部变量被称为全局变量p254)的链接性为外部的,但const全局变量的链接性为内部的。即,全局const定义就像使用了static说明符一样。

 如果处于某种原因,程序员希望某个常量的链接性为外部的,则可以使用extern关键字来覆盖默认的内部链接性

extern const int states = 50;//链接性为外部

在这种情况下,必须在所有使用该常量的文件中使用extern关键字来声明它。

在函数或代码块中声明const时,其作用域为代码块

 

8.函数和链接性 p261

所有的函数的存储持续性自动为静态的,即在整个程序执行期间都一直存在。

默认情况下,函数的链接性为外部的,既可以在文件间共享。可以在函数原型中使用extern关键字来指出函数实在另一个文件中定义的。

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

static int private(double x);
...
static int private(double x)
{
...
}

 注意,引用外部函数声明时,可以不加extern,一篇解释如下:

函数,函数,对于函数也一样,也是定义和声明,定义的时候用extern,说明这个函数是可以被外部引用的,声明的时候用extern说明这是一个声明。 但由于函数的定义和声明是有区别的,定义函数要有函数体,声明函数没有函数体,所以函数定义和声明时都可以将extern省略掉,反正其他文件也是知道这个函数是在其他地方定义的,所以不加extern也行。两者如此不同,所以省略了extern也不会有问题。

转自https://www.cnblogs.com/renyuan/archive/2012/11/30/2796801.html

9.存储方案和自动分配 p262

1)通常,编译器使用三块独立的内存:一块用于静态变量,一块用于自动变量,一块用于动态存储(动态内存,由new和delete管理)。

使用c++ new运算符申请的内存叫动态内存;存储方案的概念不适用于动态内存,但适用于用来跟踪动态内存的自动和静态指针变量。

float * p_frees = new float[20];

上述声明由new分配80个字节(假设float为4个字节)的内存将一直保留在内存中,直到使用delete运算符将其释放。但当包含该声明的语句块执行完毕时,p_frees指针将消失。

2)定位new运算符 p263

定位new运算符能够指定要使用的位置。要使用定位new特性,首先需要包含头文件new,然后将new运算符用于提供了所需地址的参数。

#include <iostream>
#include <cstring>
#include <new>
using namespace std;
char buffer1[100];

int main()
{
    double * pd1;
    pd1 = new(buffer1) double[10];//new定位特性
    cout<<"sizeof buffer1:"<<endl;
    cout<<sizeof(buffer1)<<endl;
    cout<<"strlen of buffer1:"<<endl;
    cout<<strlen(buffer1)<<endl;
    *pd1 = 5.5;
    cout<<"strlen of buffer1:"<<endl;
    cout<<strlen(buffer1)<<endl;
    cout<<"buffer1:"<<endl;
    cout<<buffer1<<endl;
    cout<<"pd1[0]:"<<endl;
    cout<<pd1[0]<<endl;
    buffer1[0] = 'a';
    cout<<"after input buffer1[0] = 'a',strlen of buffer1:"<<endl;
    cout<<strlen(buffer1)<<endl;
    cout<<"after input buffer1[0] = 'a',pd1[0]:"<<endl;
    cout<<pd1[0]<<endl;
    buffer1[1] = 'b';
    buffer1[2] = 'c';
    buffer1[3] = 'd';
    buffer1[4] = 'e';
    buffer1[5] = 'f';
    buffer1[6] = 'g';
    buffer1[7] = 'h';
    cout<<"after input bcdefgh,pd1[0]:"<<endl;
    cout<<pd1[0]<<endl;
   delete [] pd1;
return 0; }

 

posted @ 2022-03-15 23:02  SanFranciscoo  阅读(366)  评论(0编辑  收藏  举报