C++:存储持续性、作用域和链接性(第九章)

相关概念:

1.自动存储持续性:声明的变量的存储持续性是自动的,在开始执行其函数或代码块时被创建,执行完毕后他们使用的内存被释放。2种存储持续性为自动变量
2.静态存储持续性:在函数外定义变量或使用关键字static定义的变量。在程序整个运行过程中都存在。3种存储持续性为静态的变量。
3.线程存储持续性:多核处理器,CPU同时执行多个任务。暂时不学。
4.动态存储持续性:用new运算符分配的内存将一直存在,直到delete将其删除。存储在heap中。
5.作用域:简单来说就是valid的范围,分为局部作用域和全局作用域。
6.链接性(linkage):名称如何在不同单元间共享。链接性为外部的名称可在文件间共享,为内部的名称只能有一个文件中的函数共享。自动变量名称无linkage。

1. 自动存储持续性

1.默认情况下,函数中声明的函数参数和变量的存储持续性为自动,作用域为局部,没有linkage。若有相同的名称且作用域范围并不全不相同,则有最近代码块优先原则。新定义的名称将隐藏以前的定义。但是跳出新定义名称的作用域之后,以前定义的名称又可以使用。两个定义互相独立
2.自动变量和栈
栈的默认长度取决于实现,程序使用两个指针来跟踪栈,一个指针指向栈底,一个指向栈顶(下一个可用单元)。当函数被调用时,自动变量加入到栈,栈顶指针指向变量后面的下一个可用内存单元。函数结束时,栈顶指针被重置为函数被调用前的值。注意新值没有被删除,只是不再被标记。下次使用这个空间的时候他们会被覆盖。
3.寄存器变量
关键字register,建议编译器使用cpu寄存器来存储自动变量,旨在提高访问变量的速度。

//关于自动变量相同名称和作用域
int num=10;
{ 
        cout<<"1.  num is "<<num<<endl;
        int num=5, num2=9;
        cout<<"2.   num is "<<num<<endl
        <<"    num2 is "<<num2<<endl;
}
cout<<"3.   num is "<<num<<endl;
//此时若打印cout<<num2<<endl;将发生错误
return 0;
}

输出结果为:


输出结果

在代码块内部,新定义的变量覆盖了原来定义的变量,但是执行完代码块跳出,原来定义的变量又上位了,而且不能使用num2了,已经被销毁了。

2. 静态持续变量:

3种链接性:

  • 外部链接性(可在其他文件访问),
  • 内部链接性(只能在当前文件访问)
  • 无链接性(只能在当前函数或代码块访问)。
    由于静态变量的数目在程序运行期间是不变的,所以不需要特殊装置如栈来管理。如果没有显示初始化静态变量,系统默认设置为0.
    声明方式:
    1.外部链接性变量:在代码块外面声明。
    2.内部链接性变量:代码块外部用static声明。
    3.无链接性变量:代码块内部static声明。
    4.静态变量初始化:分为常量表达式初始化(静态)和动态初始化。

①. 静态持续性、外部链接性:

1.单定义规则(One Definition Rule, ODR):变量只能有一次定义,但可能多个文件要使用同一个变量,所以C++提供了2种变量声明:一是定义声明,给变量分配内存空间;另一个是引用声明,不给变量分配内存空间。referencing declaration使用关键字extern,且不进行初始化,否则将声明为定义。
2.全局变量写出来的程序并不总是可靠。正常情况下应使用局部变量,在知晓的情况下才传递数据。但全局变量也有advantage,尤其适用于表示常量数据如十二个月等,使用const来防止篡改。

下面演示声明外部链接全局变量、动态初始化、常量表达式初始化、引用声明、外部链接性设置为内部链接性

// text1.cpp 
#include<iostream>
using namespace std;
int x=3; 
int mul(int num1,int num2); 
int y = 10; // const-expression initialization
void show();

int main()
{
    const int z = mul(x,y); // dynamic-initialization 
    cout<<"x is "<<x<<endl
    <<"y is "<<y<<endl
    <<"z is "<<z<<endl;
    show();
    return 0;
}
#include<iostream>
using namespace std;
extern int x;    //引用声明
static int y=4;    //设置为内部链接
int mul(int num1,int num2); 
void show();

int mul(int num1,int num2)
{
    return x*(num1+num2);
}
 
void show()
{
    cout<<"internal y is "<<y<<endl;
}

PS 函数原型在两个源代码文件中都要有,因为没有head file。
如果报错说编译不成功,试着在项目中移除文件并重新添加。
而且要注意尽量编写完成再编译。

运行结果如下:
静态变量

②静态持续性、内部链接性:

将static限定符用于作用于为整个文件的变量时,该变量的链接性为内部。如果要在不同文件中使用相同的名称来表示变量(这个名称变量在其他文件已经定义为外部链接变量),则声明方法为static,指明该标识符的链接性为内部。
(声明方法上文图已写出)

③静态持续性、无链接性:

在代码块中用static限定符定义变量,将导致局部变量的存储持续性为静态。当代码块不处于活动状态时,该变量也存在。非常适用于两次函数调用之间,其值是不会变的。初始化过一次该变量,以后将不会像自动变量一样被初始化了。

#include<iostream>
using namespace std;
void func();
int main()
{
    for(int i=0;i<10;i++)
        func();
    return 0;
}

void func()
{
    static int times=0;  //静态持续变量 ,无链接性
    cout<<"times is now: "<<times++<<endl;
}

运行结果为
无链接性静态持续变量

3. 说明符和限定符

①存储说明符(C++11)
register、 static、 extern、 thread_local(线程)、 mutable
cv限定符:const和volatile
volatile表示即使程序代码没有对内存单元进行修改,其值也可能发生变化【暂时不学】
mutable(可变的)
用来指出即使结构或类的变量为const,某个成员也可以被修改,定义在被修改成员之前。
const
全局const int x=10 equal to static int x=10;
因为假设在头文件中有如上声明,然后每个源代码文件预处理器都将头文件的内容包含到源代码文件中,如果const声明的链接性是外部的,则违反了ODR。
如果程序员希望某个常量的链接性为external,则用extern关键字来覆盖默认的内部linkage,
extern const int y=50;

4.函数和链接性:

所有函数的存储持续性都自动为静态。默认情况下,函数的链接性为外部。也可以使用static将函数的链接性设置为内部,但必须同时在原型和定义中使用该关键字。非内联函数也适用于ODR,但内联函数不受这项规则约束。

5.语言链接性(重载函数的原理)

concept:同一个名称可能对应多个函数,必须将这些函数翻译成不同的符号名称。如C++可能将spiff(int)翻译成_spiff_i,而将spiff(double,double)翻译成_spiff_d_d,这就叫做语言链接性。要使用C语言链接来查找函数或者是C++(两种链接性不同),有如下约定:
extern “C” void spiff(int);
extern void spiff(int) = extern “C++” void spiff(int) (显示指出)

6.存储方案和动态分配

由new分配适当的字节的内存将一直保存在内存中,直到使用delete运算符将其释放。但当包含该声明的语句块执行完毕时,指向该内存的指针将消失。如果希望下一个函数能使用到该内容,则必须将其地址传递或返回给该函数。但如果声明的指针为外部的,则可以一直使用。

关于new的初始化:

int *pi = new int (6); //初始化*pi为6
int *pt = new int [4] {1,2,3,4}; //初始化数组

struct where{double x, double y, double z};
where * pr = new where {1.0,2.0,3.0}; //初始化结构
delete pi;
delete [] pt; //删除数组内存
delete pr;

定位运算符:

new负责在堆heap中找到一个足以满足要求的内存块,也可以让你能够指定要使用的内存块(定位new运算符)。
要使用此特性首先要包含new头文件。
使用定位new,指定的内存是静态内存,而delete只能用于指向常规new运算符分配的heap memory,所以不能够delete定位new运算符

下面写一段代码,通过比较地址来认识常规new运算符和定位new运算符:

#include<iostream>
#include<new>
using namespace std;
const int BUF=600;
char buff [BUF];  //这是定位new运算符将要指定的地址
int main()
{
    const int n=5;
    int * pt1, *pt2,i;
    pt1 = new int[n];
    pt2 = new (buff) int[n]; //指定地址用这样的形式声明
    cout<<"内存地址:\n"<<"heap address: "<<pt1
    <<"; static address: "<<(void *)buff<<endl;
    for(i=0;i<n;i++)
    {
        pt1[i] = pt2[i]= i;
        cout<<pt1[i]<<" at "<<&pt1[i]<<"; ";
        cout<<pt2[i]<<" at "<<&pt2[i]<<"; \n";
    }
    delete []pt1;
    int *pt3, *pt4;
    pt3 = new int[n];
    pt4 = new (buff+1) int[n];
    cout<<"内存地址:\n"<<"heap address: "<<pt3
    <<"; static address: "<<(void *)(buff+1)<<endl;
    for(i=0;i<n;i++)
    {
        pt3[i] = pt4[i]= i;
        cout<<pt3[i]<<" at "<<&pt3[i]<<"; ";
        cout<<pt4[i]<<" at "<<&pt4[i]<<"; \n";
    }
    delete [] pt3;  
    return 0;
}

运行结果如下:


定位new运算符和new存放内存理解

可以看到heap的地址为1520结尾,指定的地址为7040结尾,每个int占4字节。再第一次分配完之后delete pt1之后,堆同样的地址又被占领。(下面是第一次分配完之后不释放pt1内存单元)

image.png

发现没有释放内存单元,heap将继续往下堆砌pile。

总结:

image.png
  • 存储持续性就是表示变量在程序生存的状态,
    要么执行完一个范围(代码块或函数)就见光死(Auto)
    要么在此程序运行期间都坚强地活着或多个文件间自由穿梭(Static)
    要么我要你有你才有,我要你亡say 拜拜(dynamic)
  • 作用域就是变量可以发挥作用的区域
    小明星就在小区域(局部,代码块和函数)
    大明星就在大区域(多个文件)
  • 链接性就是你能否有足够的权利去走到更远的地方
    无链接性那你只能在代码块或函数内活动
    内部链接性那你可以在整个程序内活动
    外部链接性则可以穿梭于多个文件
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354