- 存储持续性决定数据保存在内存中的时间长度。有四种,自动存储持续性、静态存储持续性、线程存储持续性、动态存储持续性。
- 作用域描述名称在文件的多大范围内可见。有两种,代码块作用域、文件作用域。
- 链接性描述了名称如何在不同单元间共享。有三种,无链接性、内部链接性、外部链接性。
1 变量的自动存储持续性
- 默认情况下,在函数中声明的变量(包括函数参数)的存储持续性为自动的,作用域为局部,无链接性。
- 自动变量只在包含它们的函数或代码块中可见。
- 自动变量在栈上存储,函数被调用时,自动变量入栈,函数返回时,栈顶指针被重置为调用函数前的值,这些自动变量占用的空间重新被标记为可用,但值没有被删除。
2 变量的静态存储持续性
- 静态存储变量在整个程序执行期间都存在,寿命为程序的寿命,就算函数没有执行,无链接性部的静态存储变量也在内存中。
- 如果没有初始化静态变量,编译器将把它设为0。
- 静态变量的数目在程序运行期间固定不变,编译器分配固定的内存块来存储所有的静态变量。
2.1链接性为外部的静态存储变量(全局变量,外部变量)
- 链接性为外部的静态存储变量可以在其它文件中访问,在代码块外面声明创建它。
- 持续性静态,链接性外部,作用域文件。
- 单定义规则(ODR):变量只能有一次定义,所以不要在头文件中定义变量,可以声明变量!
- C++有两种变量声明:定义声明(给变量分配存储空间),引用声明(不分配空间,而是引用已有的变量)。引用声明用关键字extern,且不能初始化,否则声明成为定义,导致分配空间。
- 在多个文件中使用外部变量时,只需在一个文件中定义,在其它的文件中用extern声明它,即可在声明过的文件中使用,可以对外部变量进行读写。
- 自动变量会隐藏同名的全局变量,使用作用域解析运算符::放在变量名前面表示使用全局的版本。
int rrr = 10;
int ppp = 20;
#include <qDebug>
extern int rrr;
extern int ppp;
int main()
{
int ppp = 30;
qDebug() << "global: " << ::ppp << "local: " << ppp;
qDebug() << rrr++;
qDebug() << rrr;
return 0;
}
![在这里插入图7777片描述](https://i-blog.csdnimg.cn/blog_migrate/8fd4d44dd16313f8c21019e65d9ad0a6.png)
2.2链接性为内部的静态存储变量
- 链接性为内部的静态存储变量只能在当前文件中访问,在代码块外面声明并用static限定符创建它。
- 持续性静态,链接性内部,作用域文件。
- 链接性为内部的静态存储变量可以隐藏同名的外部变量。
- 利用链接性为内部的静态存储变量可以实现在同一文件的不同函数间共享数据,不用担心与其他文件中的变量名出现冲突。
#include <qDebug>
static int aaa = 100;
void func1();
void func2();
int main()
{
qDebug() << aaa;
func1();
qDebug() << aaa;
func2();
qDebug() << aaa;
return 0;
}
void func1()
{
aaa+= 100;
}
void func2()
{
aaa-= 100;
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/e3d622784098d599445ef49456dd6f41.png)
2.3链接性为内部的静态存储变量
- 无链接性部的静态存储变量只能在当前函数或代码块中访问,在代码块内声明并用static限定符创建它。
- 持续性静态,无链接性,作用域代码块。
- 此时虽然该变量只在代码块内可用,但在代码块处于不活动状态时仍然存在。因此在两次函数调用之间,静态局部变量的值保持不变。
- 静态局部变量只在程序启动时初始化一次,之后再次调用该函数时,它不会被再次初始化。
#include <qDebug>
int main()
{
for(int i = 0;i < 10;i++)
{
static int aaa = 10;
aaa -= 1;
qDebug() << "aaa = " << aaa;
}
return 0;
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/2c12c70227ff4184fcda355cd776c943.png)
3 函数的链接性
- C++中所有函数的存储持续性都是静态的,即在整个程序的执行过程中函数都是存在的。
- 默认情况下,函数的链接性为外部的,即可以在文件间共享。使用extern关键字声明函数指出该函数在其他文件中定义,不过加这个关键字是可选的。要让程序在其他文件中查找函数,该文件必须作为程序的组成部分被编译,或是在链接器指定搜索的库文件中。
int plus(int a,int b)
{
return (a + b);
}
#include <qDebug>
extern int plus(int a,int b);
int main()
{
int c = plus(1,2);
qDebug() << c;
return 0;
}
- 因为extern加不加都可以,所以我们一般把函数的声明放在.h文件中,把定义放在.cpp文件中。包含了函数头文件后,main函数知道它是在其他文件中定义的函数,就会到其它程序文件或库文件中查找。
- C++到哪里查找函数?如果文件中的函数声明指出该函数是静态的,则只该文件中查找函数定义,否则会在所有文件或库中查找。有些编译器,如gcc,会要求我们显式指出要搜索哪些库。
- 我们也可以使用static关键字指出函数的链接性为内部,函数只能在该文件中使用,我们必须同时在函数的声明和定义处使用这个关键字。静态函数将覆盖同名的外部定义。
- 内联函数inline不受单定义规则的限制,所以我们可以把内联函数的定义放在头文件中,每个包含了头文件的文件都有定义,然而C++要求同一个函数的所有内联定义都相同。
- 注意!!!!!!在linux内核代码中有大量的static inline定义函数。这是什么意思?????
由于inline函数不像一般函数那样放在.h和.cpp文件中调用,内联函数定义与实现要放在同一文件中。linux内核用static inline定义函数,放在头文件中,使函数仅在该文件内部可见!!在不同.cpp文件中包含后,会将代码插入到调用它的地方。在实现了静态函数隔离性好的同时,还能使用inline节省函数调用和返回时间的特性。
4 语言的链接性
- 链接程序要求不同的函数有不同的符号名,在C语言中,一个名称只对应一个函数.为满足内部需要,C编译器可能把spiff这样的函数名翻译为_spiff,这称为C语言链接性。在C++中由于函数重载,C++编译器会进行名称修饰,将不同参数的函数名翻译为不同函数名,如spiff(int)转换为_spiff_i,spiff(double,double)转化为_spiff_d_d,这称为C++语言链接性。
- C++链接程序在C库预编译的函数中寻找与C++调用匹配的函数时,规则可能不同。链接程序希望查找名为_spiff_i的,但C库中只有_spiff的。为解决这个问题,我们用函数原型来指出使用的约定。
- 在库中查找函数时使用指定的语言链接性
extern "C" void spiff(int);
extern "C++" void spaff(int);
extern void spoff(int);