从C到Cpp——十、内存模型

一、头文件

可以把下列内容放在头文件之中:

1-函数原型

2-结构声明

3--类声明

4-模板声明

5-内联函数(有特殊的链接属性)

6-使用#define或者const定义的符号常量(后者有特殊的链接属性)

在源文件中使用头文件时,需要#include "name_of_headfile.h"。比如,头文件名称为head,则在源代码第一行写

#include "head.h"

同一个文件只能只能包含同一个头文件一次,为避免重复包含某个头文件,采用以下方式进行规避

#ifndef HAVE_USED_HEAD
#define HAVE_USED_HEAD
statement
#endif

为理解这一段代码,首先看#define HAVE_UESD_HEAD。虽然HAVE_USED_HEAD没有被定义为任何值,但是这个定义是有效的,它把HAVE_USED_HEAD当作了一个标签。

#ifndef HAVE_USED_HEAD 则会检测HAVE_USED_HEAD是否被定义,如果已经被定义,就忽视#ifndef和#endif之间的代码;否则,会对两者之间的代码进行编译

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

储存持续性描述了变量在程序中存在时间的长短,作用域描述了名称在文件中多大范围内可见,链接性则描述了名称在不同单元里共享的性质。

这么说确实很难理解,举例而言,在main函数中定义int a,那么a这个变量的储存持续性指的是它的分配给它的内存持续多长时间,是在程序运行的时候都有分配内存还是仅在运行main函数时分配内存?a这个变量的作用域指的是a能在哪些地方使用,是在main函数中能使用还是在其他函数中也能使用?链接性是这个变量能否进行共享,对于a,也就是在主函数外部能不能使用呢?(三个概念好像有一些似是而非的地方,不用太过区分)

储存持续性包括以下几种:

1-自动储存持续性:在函数(包括形参)或代码块中定义的变量具有自动储存持续性,程序开始执行所属函数或代码块(花括号括起来的代码段)时被创建,函数或代码块结束时,内存被释放。

2-静态储存持续性:在函数定义外定义的和使用关键字static定义的变量存储持续性为静态,它们在程序运行的整个过程都存在。

3-动态储存持续性:用new分配的内存,在用delete释放前或者程序结束前都存在。

4-线性储存持续性(C++11):用于并行编程

作用域包括局部全局,作用域为局部的变量只能在代码块中使用,作用域为全局的能在整个程序中使用

链接性包括下列三种
1-无链接性:不能共享

2-内部链接性:只能由同一源文件的不同函数使用

3-外部链接性:可以由同一项目的不同文件使用

三、5种变量储存方式

5种变量储存方式

 

四、自动变量的要点说明、寄存器变量

1--不会零初始化(创建变量时,自动把初始值设为0)

2--不仅在主函数和自定义函数中可以定义,而且在代码块比如if中也能使用,但做作用域仅在代码块中

int main(void)
{
    if (1)
    {
        int a=1;
        cout<<a<<endl;
        //这一句是合法的!
    }
    cout<<a<<endl;
    //这一句就非法了!
}

3--作用域小的自动变量会在所在作用域中覆盖作用域大的同名自动变量,自动变量会覆盖静态变量

#include <iostream>
using namespace std;
int a=1;
int main(void)
{
	int a=2;
	cout<<a<<endl;
	if(1)
	{
		int a=3;
		cout<<a<<endl;
	}
} 

(这段程序的输出是2和3,印证了上述性质)

4--所有的自动变量可以加上register限定符变为寄存器变量,它建议编译器用CPU寄存器来储存自动变量,可以有更高的访问变量效率。使用方法如下:

register int a=1;

五、静态变量的要点说明

1--所有的静态变量都会零初始化

2--在函数内部或代码块中使用static,效果和自动变量一样,不同的是内存空间不会在代码块或函数结束时被释放!但在代码块或函数之外,也不能使用这个变量了!(依靠指针喽~)

3--在函数外部使用static作用:

如果像下面这样使用,则变量a的链接性为内部,虽然程序运行时该变量始终存在,但仅在该源文件中可以使用变量a

#include <iostream>
static int a;
int main(void)
{    
    statement;
}

如果像下面这样使用,则变量a可以在同一项目的多个文件中使用

​​#include <iostream>
int a;
int main(void)
{    
    statement;
}

4--在别的源文件中使用静态变量

有趣的是,在同一项目下的源文件b中不能直接使用源文件a中定义的静态变量。必须加上extern限定符,这相当于是一个说明。

加入变量x已经被源文件a定义,则在同一项目下的源文件b中应该这么写:

extern int a;

5--extern加上初始化

如果extern限定的变量进行了初始化,那么它相当于定义了外部链接性的静态变量,所以以下两种写法等价!

int a;
int main(void)
{
    ......
}
extern int a=0;
int main(void)
{
    ......
}

6--单定义规则

在同一项目中,同名的外部链接性静态变量只能定义一次。源文件a和b都在函数外直接定义int x就会报错!

但是呢,如果源文件a中定义int x,而源文件b中定义static int x是可行的。源文件b中后者会暂时覆盖前者,直到源文件b结束!

六、其他限定符

register、static和extern都是定义变量的限定符,下面还有几种别的限定符:

1--const

内存被初始化后,程序不能对其修改

2--volatile

告诉编译器即使程序代码没有对该变量的内存单元进行修改,其值也会发生改变。常用于接收串口信息,防止编译器的优化造成错误。

3--mutable

用于指出即使结构(类)变量(对象)为const,其某个成员仍然可以被修改

七、const限定的静态全局变量的特殊链接性!

在函数外声明const变量,它会自动的具有内部链接性,也就是说下列两种写法等价:

const int a=10;
int main(void)
{
    ......
}
static const int a=10;
int main(void)
{
    ......
}

为什么要这么设计呢?因为C++的设计者希望程序员把const变量放到头文件中,这样设计可以避免头文件中的const变量违背单定义规则!

当然,如果你希望const变量具体外部链接性,可以采用extern加初始化的办法

extern const int a=10;

八、函数的链接性

1--自定义函数默认是具有外部链接性的,即源文件a中定义的函数可以在源文件b中使用,函数声明(原型)和函数定义中同时使用关键字static可以使函数只有内部链接性,即只能在某一个源文件中使用

static void nbfunc();
......
static void nbfunc()
{
......
}

2--和变量一样,静态函数(也就是上面用static定义的函数)也满足覆盖的规则,如果源文件b中定义了某个函数,即使同一项目下的源文件a中已经定义了同名的函数,也不会被源文件b使用!

3--非内联函数也满足单定义规则,除非进行函数重载,不然同一项目下的源文件a和b不能同时定义同名函数!

4--内联函数的链接性使内部的,所以它不用满足单定义规则,这么做意味着它和const变量一样最好放在头文件中定义!

九、new运算符和动态储存空间

1--

动态内存由new和delete控制,而不是由作用域和链接性规则控制。但用于动态内存分配的指针仍然是由作用域和链接性控制的,动态内存如何使用主要看指针是如何定义的!

2--new运算符的初始化

为内置的标量类型分配空间和初始化,可以如下操作,加上圆括号或花括号(仅C++11):

​int *p=new int (3);
double *a=new double(1.5);
int *p=new int {3};
double *a=new double{1.5}
​

初始化结构和数组,也是允许的!

struct hello{int a,int b};
int* a=new int[4] {1,2,3,4};
hello* b=new hello {1,2};

3--new失败

在老版本。会返回NULL指针,在新版本中,会引发std::bad_alloc异常!

4--new的实现其实是运用了运算符的重载!

5--new的变种——定位new运算符

包含头文件new,也即#include<new>时可以使用定位new运算符,定位new运算符需要含有参数,用圆括号括起来,参数是指向某个储存空间的指针!

char name[40];
int* pname=new(name) int[10]

这么写,可以让编译器把name空间的内存分配给pname!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值