C++ Primer Plus(嵌入式公开课)---第9章 内存模型和名称空间

0318

C++ Primer Plus - 第九章

第八章 函数探幽

点这里

第九章 内存模型和名称空间

9.1 单独编译

大多数C++环境都提供了其他工具来帮助管理,UNIX和Linux系统都具有make程序,可以跟踪程序以来的文件以及这些文件的最后修改时间。运行make时,如果它检测到上次编译后修改了源文件,make将记住重新构建程序所需的步骤。

头文件 #include "123.h"

原来的一个程序可以分成三部分:

  • 头文件:包含结构体声明和使用结构体的函数的原型;
  • 源代码文件:包含(与结构体有关的)函数的代码(函数定义);
  • 源代码文件:包含调用(与结构体有关的)函数的代码。

头文件中常包含的内容:

  • 函数原型;
  • 使用#define 或 const定义的符号常量;
  • 结构体声明;
  • 类声明;
  • 模板声明;
  • 内联函数。
为什么是双引号,不是尖括号?

在包含头文件时,我们使用"123.h",而不是<123.h>
因为如果文件名包含在尖括号中,则C++编译器将在(存储标准头文件的)主机系统的文件系统中查找;
但如果文件名包含在双引号中,则编译器将首先查找当前的工作目录或源代码目录

头文件管理 #ifndef(if not defined)

在同一个文件中,只能将同一个头文件包含一次,但是很可能在不知情的情况下将头文件包含多次。力图,可能使用包含了另外一个头文件的头文件。有一种标准的C/C++技术可以避免多次包含同一个头文件,它是基于预处理器编译指令#ifndef的(if not defined)。

头文件coorden.h

#ifndef COORDIN_H_
#define COORDIN_H_  //定义名称COORDIN_H_  

struct polar{
   
	double distance;
	double angle;
};
struct rect{
   
	double x;
	double y;
};
polar rect_to_polar(rect xypos);
void show_polar(polar dapos);

#endif

编译器首次遇到该文件时,名称COORDIN_H_没有被定义,编译器将查看#ifndef#endif之间的内容,并读取定义COORDIN_H_的一行。如果在同一个文件中遇到其它包含coordin.h的代码,编译器将知道COORDIN_H_已经被定义了,从而跳到#endif后面。
注意:这种方法并不能防止编译器将文件包含两次,只是让它忽略除第一次包含之外的所有内容。大多数标准C/C++头文件都是用这种防护方案

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

C++笔记3:C++核心编程 --> 1、内存分区模型
C++ Primer Plus(嵌入式公开课)—第4章 复合类型–>4.8.5 自动存储、静态存储和动态存储

9.2.0 存储持续性

  • 自动存储持续性:函数参数、函数定义中声明的变量(局部变量) 的存储持续性是自动的。它们在程序开始执行其所属的函数或者代码块时被创建,在执行完函数或代码块时,它们使用的内存被释放
  • 静态存储持续性:在函数定义外定义的变量(全局变量)、使用关键字static定义的变量(静态变量) 的存储持续性是静态的。它们在程序的整个运行过程中都存在
  • 动态存储持续性:用new运算符分配的内存将一直存在,直到使用delete运算符将其释放或者程序结束为止。这种内存的存储持续性是动态的,被称为 自由存储
  • 线程存储持续性(C++11):如果变量使用关键字thread_local声明,则其生命周期与所属的线程一样长

9.2.1 作用域与链接

作用域(scope)描述了名称在文件的多大范围内可见:

  • 作用域为局部的变量只能在定义它的代码块中使用;—代码块
  • 作用域为全局的变量在定义位置到文件结尾之间都可用;—单个文件
  • 自动变量的作用域为局部;—代码块
  • 静态变量的作用域是全剧还是局部取决于它是如何被定义的;—static
  • 函数原型中定义的名称只在包含参数列表的括号内可用,这就是为什么 这些名称是什么以及是否出现 都不重要的原因;
  • 中声明的成员的作用域为整个类;
  • 名称空间中声明的变量的作用域为整个名称空间。

链接性(linkage)描述了名称如何在不同单元间共享:

  • 链接性为外部的名称可在文件之间共享
  • 链接性为内部的名称只能由一个文件中的多个函数共享
  • 自动变量的名称没有链接性,因此他们不能共享,只能在它所属的函数或者代码块中使用
文件、程序、代码块、函数

代码块:由花括号括起的一系列语句。例如,函数体就是代码块,但可以在函数体中嵌入其他代码块。
文件: 123.h 123.cpp main.cpp 头文件 源程序文件。
程序:一个程序可由多个文件组成。

9.2.2 自动存储持续性(局部变量)

局部变量:函数中声明的函数参数变量。存储在区。

  • 存储持续性:自动
  • 作用域:局部(函数或代码块内)
  • 链接性:没有链接性
C++11 中的 autoregister

9.2.7 说明符和限定符

局部变量存储在栈区中

程序使用两个指针来跟踪栈:

  • 一个指针指向栈底—栈的起始位置;
  • 另一个指针指向栈顶—下一个可用内存单元。

当函数被调用时,其自动变量将被加入到栈中,栈顶指针指向变量后面的下一个可用的内存;
函数结束时,栈顶指针被重置为函数被调用前的值,从而释放新变量使用的内存。这些新变量并没有被删除,只是不再被标记,他们所占据的空间将被后来加入到栈中的局部变量所覆盖。
在这里插入图片描述

9.2.3 静态持续变量—关键字static

静态变量:函数中声明的函数参数变量。存储在区。

  • 存储持续性:静态,在整个程序执行期间一直存在。
  • 作用域:多个文件、单个文件、函数或代码块内
  • 链接性:外部链接性、内部链接性、没有链接性

未初始化的静态变量的所有位都被设置为0

外部链接性:可在其他文件中访问;
内部连接性:只能在当前文件中访问;
无链接性:只能在当前函数或代码块中访问。

创建链接性为外部的静态持续变量,必须在代码块的外面声明它;
创建链接性为内部的静态持续变量,必须在代码块的外面声明它,并使用static限定符;
创建没有链接性的静态持续变量,必须在代码块内声明它,并使用static限定符。

在这里插入图片描述

变量名 作用域 链接性 存储持续性
global 多个文件 外部 静态
ont_file 单个文件(当前文件) 内部 静态
count 代码块/函数 无链接性 静态
llama 代码块/函数 无链接性 自动(局部变量)

变量count 和 llama的区别:(9.2.6 静态局部变量 和 局部变量 的区别)

  • 相同点:二者都只能在funct1()中使用;
  • 不同点:即使funct1()函数没有被调用,count也留在内存中;而llama只有在funct1()函数被调用时才会为其分配内存,当函数结束时,此内存会被释放。
☆☆☆☆☆总结:常规外部变量、静态外部变量、静态局部变量、局部变量

1.常规外部变量(函数外定义,不加static),全局变量,多个文件可访问—9.2.4
2.静态外部变量(函数外定义,加static),全局变量,当前文件可访问;—9.2.5
3.静态局部变量(函数内定义,加static),全局变量,代码块内有效;—9.2.6
4.局部变量(函数内定义,不加static),局部变量,代码块内有效。

其中123是静态变量,4是自动变量,还有一个9.2.10的动态内存。

全局变量是静态变量,存储在全局区;局部变量是自动变量,存储在栈区。

变量 存储描述 作用域 链接性 存储持续性 如何声明
局部变量 自动 代码块/函数 无链接性 自动 在代码块中
局部变量 寄存器 代码块/函数 无链接性 自动 在代码块中,使用关键字register
静态(局部)变量 静态,无链接性 代码块/函数 无链接性 静态 在代码块中,使用关键字static
常规外部变量 静态,外部链接性 多个文件 外部 静态 代码块的外面
静态(外部)变量 静态,内部链接性 单个文件(当前文件) 内部 静态 代码块的外面,使用关键字static

9.2.4 静态持续性、外部链接性—关键字extern

单定义规则:变量只能有一次定义。
使用关键字extern

在这里插入图片描述
前提是静态变量,具有外部链接性,即在代码块的外面声明的变量。看个示例:

9.5.cpp

//程序清单9.5:
#include<iostream>
using std::cout; using std::endl;

double warming = 0.3;//①链接性为外部的静态变量

void update(double dt); //②
void local();			//③

int main(){
   
    cout << "main warming = " << warming << endl;//0.3
    update(0.1);//④
    cout << "main warming = " << warming << endl;//0.4
    local();	//⑤
    cout << "main warming = " << warming << endl;//0.4

    return 0;
} 

9.6.cpp

//程序清单9.6:
#include<iostream>
using std::cout; using std::endl;
extern double warming;//⑥

void update(double dt);
void local();

void update(double dt){
   
    extern double warming;//⑦这行代码可有可无
    warming += dt;
    cout << "update warming = " << warming << endl;//0.4
}
void local(){
   
    double warming = 0.8;//⑧
    cout << "local warming = " << warming << endl;//0.8   
    cout << "local ::warming = " << ::warming << endl;//⑨0.4
}

分析:
1.定义一个全局变量warming,链接性为外部,即可被多个文件访问;
2.3.函数声明;—这个不可少
4.5.函数调用;
6.使用关键字extern声明变量warming,让该文件中的函数都能够使用它;
7.再次使用关键字extern声明变量warming,这句可有可无,因为已经有⑤了。
8.在local()函数中定义了一个与全局变量warming同名的局部变量,因此在此函数中,这个局部变量将隐藏全局变量;但依然可以利用::来调用全局变量(下面第8点);
9.利用作用域解析运算符::表示使用变量的全局版本–>9.3 的 局部名称隐藏名称空间名?

结果:
在这里插入图片描述

多文件编译 g++ 9.5.cpp 9.6.cpp ☆☆☆
g++ 9.5.cpp 9.6.cpp
./a.cout

如果只在含有main函数的程序中按F5,就会编译出错或者没结果,因为这样只会编译当前文件,而不会链接到其他文件。

作用域解析运算符::

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

总结:

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

9.2.5 静态持续性、内部链接性

如果文件1中定义了一个常规外部变量errors(外部链接性),
而文件2中定义了一个同名的静态(外部)变量(内部链接性),则在文件2中,静态(外部)变量将隐藏常规外部变量。

看个示例:
9.7.cpp

//程序清单9.7:
#include<iostream>

using std::cout; using std::endl;

int tom = 3;            //常规外部变量---全局变量,多个文件可访问
int dick = 30;          //常规外部变量---全局变量,多个文件可访问
static int harry = 300; //静态外部变量---全局变量,当前文件可访问 = 300

void remote_access();

int main(){
   

    cout << "main: tom = " << tom << "; &tom = " << &tom << endl;
    cout << "main: dick = " << dick << "; &dick = " << &dick << endl;
    cout << "main: harry = " << harry << "; &harry = " << &harry << endl;
    remote_access();

    return 0;
}

9.8.cpp

//程序清单9.8:
#include<iostream>
using std::cout; using std::endl;

extern int tom;            //使用文件1中定义的常规外部变量---全局变量,多个文件可访问
static int dick = 10;          //静态外部变量---全局变量,当前文件可访问,并且会覆盖 = 30
int harry = 200;               //常规外部变量---全局变量,多个文件可访问

void remote_access(){
   
    
    cout << "remote_access: tom = " << tom << "; &tom = " << &tom << endl;//
    cout << "remote_access: dick = " << dick << "; &dick = " << &dick << endl;
    cout << "remote_access: harry = " << harry << "; &harry = " << &harry << endl;
}

分析:
1.在9.7.cpp中定义并初始化的变量tom是链接性为外部的静态变量,在9.8.cpp中想用,就需要加个关键字extern
2.在9.7.cpp中定义并初始化的变量dick是链接性为外部的静态变量,在9.8.cpp中想用,就需要加个关键字extern;但是9.8.cpp中也定义了一个同名的链接性为内部的静态变量,所以在9.8.cpp中静态(外部)变量就覆盖了常规外部变量;
3.同理,在9.8.cpp中定义并初始化的变量harry是链接性为外部的静态变量,在9.7.cpp中想用,就需要加个关键字extern;但是9.7.cpp中也定义了一个同名的链接性为内部的静态变量,所以在9.7.cpp中静态(外部)变量就覆盖了常规外部变量。

结果:
在这里插入图片描述

结论:
如果有作用域范围更小的变量,就把作用于更大的变量给覆盖了,类似于前面的局部变量覆盖全局变量。

9.2.6 静态存储持续性、无链接性

之前的一个示例:(8.8 编程练习 第1题:static变量记录函数被调用的次数

#include<iostream>

using std::cout; using std::cin; using std::endl;

void func1();//返回函数被调用的次数

int main(){
   
    
    func1();
    func1();
    func1();
    func1();
    func1();
    
    return 0;
}

void func1(){
   
    static int count = 0;//静态局部变量
    int num = 0;//局部变量
    num += 2;
    cout << "num = " << num << "; ";
    count++;
    cout << "函数被调用第 count = " << count << "次。" << endl;
}

结果:

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值