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

作用域scope描述了名称在文件(翻译单元)的多大范围内可见。

链接性linkage描述了名称在不同单元间共享。链接为外部的名称可以在文件间共享,链接性为内部的名称只能由一个文件中的函数共享。自动变量的名称是没有链接性,因为他们不能共享。

在名字空间中声明的变量的作用域为整个名称空间,因此全局作用域是名称空间作用域的特例。

寄存器变量是一种形式的自动变量,因此其存储持续性为自动,作用域为局部,但没有链接性。关键字register提醒编译器,用户希望它通过使用CPU寄存器,而不是堆栈来处理特定的变量,从而提供变量访问的快速。这种提醒并不意味着编译器一定满足这种请求。比如寄存器已经被占用,或者寄存器无法存储所请求的类型。如果变量被存储在寄存器中,那么他就没有内存地址,因此不能将地址操作符用于寄存器变量。

void gromb(int *)  

int main()
{
    int x;
    register int y ;
   gromb(&x);
   gromb(&y);    //不能使用取地址符
  ...  
5种变量存储方式
存储描述 持续性 作用域 链接性 如何声明
自动 自动 代码块 可使用auto
寄存器 自动 代码块 在代码块中使用register
静态,无链接 静态 代码块 在代码块中,使用static
静态,外部链接性 静态 文件 外部 在函数外面
静态,内部链接性 静态 文件 内部 在函数外面,使用static
int global=1000;            //static静态持续性,外部链接,可在文件外面使用
static int one_file=50;    //static静态持续性,内部链接,只能在当前文件中使用
int main()
{
   ……
}
void fun1(int n)
{
   static int count = 0;//static静态持续性,无链接性,只在函数中使用
   int llama=0;
……
}
void fun2(int q)
{
  ……
}  

这里的global具有外部链接性,因此,在另一个文件file2.cpp可以直接使用他 ,但是在使用他之前,需要提供变量的external声明


//file2.cpp
#include "iostream"
using namespace std;
external int global;     //外部变量使用前必须声明  

void showExternal()
{
   cout << global <<endl;
}
 

这里尤其要注意的是声明的两种形式

定义声明(defining declaration)或简称为定义definition,它主要给变量分配存储空间。定义只能进行一次

重新声明或称为引用声明(referencing declaration)简称为声明declaration,它不给变量分配空间,只是扩展他得作用域,因此不能在引用声明中初始化变量。声明可以多次


external double warning = 0.5//不能给引用声明初始化  

仅当声明将变量分配存储空间时,即定义声明,才能在声明中初始化变量。

说明符和限定符

除了auto,register,static,extern之外,还有const,volatile,mutable

volatile关键字仅针对const起作用,他表明,即使程序代码没有对内存单元进行修改,,其值也可能发生变化。例如,一个指针指向某个硬件位置,其中包含了来自串行端口的时间或信息。在这种情况下,硬件(而不是程序)可能修改其中的内容。或者两个程序可能相互影响,共享数据。该关键字作用是改善编译器的优化能力,在读取某个变量时,将这个值缓存到寄存器中。这种优化假设变量的值在两次使用之间不会变化。

再谈const

假设将一组常量放在头文件中,多个文件包含该头文件。这会导致重复定义常量,因为他们是内部链接性的。只能将这些常量放在一个文件中,而其他文件必须使用extern来提供引用声明。另外,只有未使用extern关键字的声明才能进行初始化。由于外部定义的const数据的链接性是内部的,因此可以在所有文件中使用相同的声明。内部链接性还意味着,每个文件都有自己的一组常量,而不是所有文件共享一组常量。每个定义都是所属文件私有的,这就是能够将常量定义放在头文件中的原因。

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


extern const int states = 50  

这点与定义常规外部变量不同,常规外部变量定义时无需extern关键字。但这里需要,切记!

函数和链接性

函数的存储持续性都是静态的,即在整个程序执行期间都是一直存在。默认情况下,函数的链接性为外部的,即可以在文件中共享。要让程序在另一个文件中查找函数,该文件必须作为程序的组成部分被编译,或者是有链接程序搜索的库文件。)还可以使用关键字static将函数的链接性设置为内部的,使之只能在一个文件中使用。如果该文件的函数原型指出该函数是静态的,则编译器将只在该文件中查找函数定义;否则,编译器(包括链接程序)将在所有的程序文件中查找。如果找到两个定义,编译器将发出错误消息,因为每个外部函数只能有一个定义,如果在程序文件中没有找到,编译器将在库中搜索。这意味着如果定义了一个与库函数同名的函数,编译器将使用程序员定义的版本,而不是库函数。

语言链接性

链接程序要求每个不同的函数都有不同的符号名。在C语言中,一个名称只对应一个函数,因此很容易实现。为满足内部需求,C语言编译器,只能将spiff这样的函数翻译为_spiff,这种方法叫C语言链接性。但C++中,同一个名称的函数可能对应多个函数,必须将这些函数翻译成不同的符号名。因此,C++编译器指向名称矫正或名称修饰,为重载函数生成不同的符号名称。例如spiff(int )转换成_spiff_i,spiff(double,double)转化成_spiff_d_d。这种称为C++语言链接。

链接程序寻找与C++函数调用匹配的函数时,使用的方法与C语言不同,但如果要在C++程序中使用C库中预编译的函数,将出现什么情况?

假设

spiff(22);//调用C库中的spiff(int)函数

它在C语言库中的符号名为_spiff,但C++查询约定的符号名_spiff_i。为解决这类问题,可以用函数原型来指出要使用的约定。


extern "C" void spiff(int );  //use c protocol for name look-up
extern void spoff(int) //use c++ protocol for name look-up
extern "C++" void spaff(int); //use c++ protocol for name look-up  

动态分配

通常,编译器使用3块独立的内存,一块用于静态变量(可能再细分,静态存储区),一块用于自动变量(栈)。一块用于动态存储(堆)。

请注意,使用new来设置指针的语句必须位于函数中,如果在函数外,就是静态存储,而静态存储,只能使用常量表达式来初始化静态存储变量。

时间: 2025-01-19 14:04:26

存储持续性、作用域和链接性的相关文章

关于存储持续性,作用域,链接性,static与extern

存储持续性:变量从声明到销毁. 自动存储持续性:自动变量(如在函数内int a;)的持续性为自动,在代码块({...}或函数内)中被创建,执行完函数或代码块后销毁,内存被释放.如在函数内(包括main)声明自动变量,则函数结束后就销毁. 静态存储持续性:在函数外(如在main之前声明int a;另外函数内不能嵌套定义,即main中是不能再定义一个函数的)或使用static定义的变量,他们的存储持续性在程序(main)运行的整个过程中都存在,但作用域会有区别.在函数外声明的变量,他的作用域(下面讲

【转】作用域、链接属性、存储类型总结--转载学习,很清晰,很详细

标识符: 首先,在讨论这三种东西之前,详细说明一下C语言中的标识符. 标识符是用户编程为变量.常量.函数.语句等指定的名字,就好比人名一样的东西. 标识符的命名规则如下: 1.只能由字母.数字.下划线组成,并且首字符不能是数字: 2.不能把C语言的关键字作为标识符: 3.对大小写敏感: 其次,需要明确,作用域和链接属性是在标识符范畴内讨论的,存储类型是在标识符中的变量范畴内讨论的. 作用域: 标识符的作用域就是程序中该标识符可以被使用的区域,由它的声明位置决定. 分为以下四类: 1.文件作用域

【编程语言】变量的存储时期/作用域以及static/extern的用法

存储类:               存储时期       链接         内存管理 对于一个变量(不同的存储类型)可以通过存储时期,链接属性,以及相应的作用域来描述它. 存储时期就是变量在内存中的保留时间,变量的作用域和链接在一起表明程序的哪些部分可以通过变量名来使用该变量.于是就有了不通过的存储时期,链接属性,以及作用域的组合 [1]存储时期(变量的生命期,表示存取一个变量在内存空间的存放以及释放时间)                           局部变量          

作用域 属性链接 存储类型

一:作用域: 4中不同类型的作用域:代码块作用域,函数作用域,文件作用域和原型作用域 1:代码块作用域: 一对花括弧的代码称作一个代码块,任何在花括弧开始声明的标识符都具有代码块作用域 形参隐藏的问题: K&RC中,形参在函数体外的声明中开始变生效,如果在函数体内有同名的表示服,他们就会将形参隐藏. ANSIC中避免了这种情况,他把形参的作用域定义在函数最外层的那个函数体,也就是整个函数体,这样,声明与函数最外的局部变量无法和形参同名,因为他们的作用域相同2:文件作用域: 任何在所有代码块之外声

关于extern和static关键字引出的一些关于作用域和链接属性和存储类型的问题

在进入正题前我们必须了解一些概念: 标识符:标识符不仅仅代表着变量的名字,main()函数的main也是一个标识符,这点很重要. 存储类型:即变量的存储位置及其生存周期:静态区:分为两块 .date 已显式初始化的全局变量了静态变量 .bss 存放未初始化的全局或者静态变量 注意:静态变量的初值是在编译时就进行初始化了:意思就是用static修饰的变量赋过数值的话就保存为他的初值,如果没有初始化的话就赋值为零,且整个程序只初始化一次:即不管static int i = 1:或者这 static

存储类、生命周期、作用域、链接域

Linux下c内存映像 大方向分为 应用空间 + 内核空间,他俩内存空间布局差不多.这里重点回顾应用空间布局,应用空间氛围代码段 + 数据段(静态数据段+动态数据段) 代码段 为啥是只读的 代码段在编译时就定好了,在程序的运行过程中,不能在代码段去开辟空间,以及释放空间. 包含哪几部分 ELF头.段头部表.init节 参考:剖析可执行文件ELF组成 .text 指令节,也叫代码节,所有函数中的指令都放在了.text节中.能够与指令直接弄在一起的常量,也随指令一起放在了.text中. .rodat

变量的存储和作用域

初始化和赋值的区别是什么? 初始化:声明变量的时候同时赋值,声明的时候会划出新的内存区域,同时 赋值: 变量的储存方式:静态储存和动态储存. 静态存储:变量定义的时候,分配了一定的内存单元,在整个程序中,内存单元都不变.只能初始化一次,可以多次赋值,静态存储变量不初始化则初始为0. 动态存储:程序执行过程中才分配内存单元,使用完后就释放,如形参.函数的形式参数就是动态存储方式,声明函数的时候不给它分配内存单元.调用的时候传入实参,就分配内存单元,然后函数执行完后,就释放内存.动态存储变量若不初始

C/C++ 存储类别

table { margin: auto; } 本文介绍 C/C++ 中的存储类别.所谓的"存储类别"究竟是什么意思? 存储类别主要指在内存中存储数据的方式,其大致牵涉到变量的三个方面 -- 作用域.链接性和存储期,也就是说这三个方面决定了存储类别.下面先解释这三个概念,再介绍在 C/C++ 中的表示形式. 存储类别定义 作用域 (scope) 描述程序中可访问变量的区域,主要有块作用域 (block scope) 变量和 文件作用域 (file scope) 变量,平常我们也分别用局

四种不同对象的生存方式(栈、堆、全局、局部静态)

[结果分析,引申出四种对象]: 生存方式 执行时机 消亡时机 全局(静态)对象 全局静态存储区global 比程序进入点更早,构造函数先被执行: 程序结束前,其析构函数被执行. 局部静态对象 局部静态存储区local static 在对象诞生时,其构造函数被执行.(注意,此处只会有一个实例产生,而且固定在内存上(非stack也非heap),它的构造函数在控制权第一次移转到其声明处时被调用. 程序将结束时(此对象因而将遭致毁灭)其析构函数才被执行,但比全局对象的析构函数更早一步执行. 局部对象 栈