《C和指针》笔记14: 作用域和存储类型总结(例子说明)

本文是作用域和存储类型的总结,以一个例子来说明,如果不看解释可以很直接地回答每一条语句的作用域和存储类型,那么说明已经很熟练地掌握这个知识点了。

关于作用域和存储类型可以参考我前面的博客:

《C和指针》笔记10:作用域
《C和指针》笔记12: 存储类型(自动变量、静态变量和寄存器变量)

题目

请看下面的代码,回答每一行的标识符的链接属性、存储类型和作用域
在这里插入图片描述

答案

行数标识符链接属性存储类型作用域
1aexternal静态存储第1-第12行,第17-第29行
2bexternal静态存储第2-第6行,第25-第29行
3cinternal静态存储第3-第29行
4dexternal静态存储第4行-文件结束
4eexternal自动存储第6-11行,第17-19行以及第23-24行
6f不具有链接属性自动存储至函数结束
7b不具有链接属性寄存器存储至函数结束
8g不具有链接属性静态存储至函数结束
9aexternal静态存储和第一行的a一样
12e不具有链接属性自动存储延伸至16行
13g不具有链接属性自动存储延伸至16行
14hexternal静态存储当前的代码块
19x不具有链接属性自动存储当前的代码块
20e不具有链接属性自动存储当前的代码块
25iinternal静态存储从它声明的位置直到这个源文件结束

解释

  • 第1行的a
    • 链接属性: external,因为它属于文件作用域的声明在缺省情况下为external链接属性,可以被其他源文件访问。
    • 存储类型静态存储
    • 作用域第1-第12行,第17-第29行。静态变量在程序执行之前创建,并一直保持它们的值,直到程序结束。当程序开始执行时,变量a将初始化为5。其作用域应一直延伸到这个源文件结束为止。但是第13行出现的a的局部变量将隐藏同名的静态变量。
  • 第2行的b
    • 链接属性: external,如果b的定义在其他地方,第2行的extern关键字在技术上并非必需,但在风格上却是加上这个关键字为好,可以被其他源文件访问。
    • 存储类型静态存储
    • 作用域第2-第6行,第25-第29行。静态变量在程序执行之前创建,并一直保持它们的值,直到程序结束。其作用域应一直延伸到这个源文件结束为止。 但是由于第7行出现的b的局部变量将隐藏同名的静态变量。
  • 第3行的c
    • 链接属性: internalstatic关键字修改了c的缺省链接属性,把它改为internal。只能被当前的源文件访问。
    • 存储类型静态存储
    • 作用域第3-第29行。静态变量在程序执行之前创建,并一直保持它们的值,直到程序结束。其作用域应一直延伸到这个源文件结束为止。
  • 第4行的d
    • 链接属性: external。作为函数名,d在缺省情况下具有external链接属性,所以其他源文件只要在文件上存在d的原型 ,就可以调用d。函数d的定义对于这个源文件中任何以后想要调用它的函数而言起到了函数原型的作用,如果我们将函数声明为static,就可以把它的链接属性从external改为internal,但这样做将使其他源文件不能访问这个函数。
    • 存储类型静态存储。对于函数而言,存储类型并不是问题,因为代码总是存储于静态内存中。
    • 作用域第4行直到文件结束。静态变量在程序执行之前创建,并一直保持它们的值,直到程序结束。其作用域应一直延伸到这个源文件结束为止。
  • 第4行的e
    • 链接属性: 不具有链接属性,所以我们只能从函数内部通过名字访问它。
    • 存储类型自动存储。在函数被调用时被创建,当函数返回时消失。
    • 作用域第6-11行,第17-19行以及第23-24行。与局部变量冲突导致了作用域的变化。
  • 第6行-第8行的f,b,g
    • 链接属性: 不具有链接属性,所以它们不能在函数的外部通过名字访问(这是它们称为局部变量的原因)
    • 存储类型
      • f是自动存储,当函数每次被调用时,它通过隐式赋值被初始化为15。
      • b是寄存器存储,所以它的初始值是垃圾。
      • g是静态存储,在程序的整个执行过程中一直存在。当函数每次被调用时,它并不会被重新初始化。
    • 作用域到函数结束为止。由于与局部变量冲突,它的作用域限于。
  • 第9行的a
    • 它的声明并不需要。这个代码块位于第1行声明的作用域之内。所以它和第一行的a的连接属性、存储类型都是一样的。
  • 第12行的-第13行的e,a
    • 链接属性: 不具有链接属性,和先前声明的a和e不同,在这个代码块中,以前声明的同名变量是不能被访问的。
    • 存储类型自动存储
    • 作用域延伸至第16行
  • 第14行的h
    • 链接属性: external。相当一个全局变量。这是唯一一个必须使用extern关键字的声明,如果没有它,h将变成另一个局部变量。
    • 存储类型静态存储
    • 作用域当前的代码块
  • 第19和20行的x,e
    • 链接属性: 不具有链接属性,和先前声明的a和e不同,在这个代码块中,以前声明的同名变量是不能被访问的。
    • 存储类型自动存储
    • 作用域当前的代码块

这里的e和第4行函数的形参e是不同的变量,它和第12行声明的e也不相同。在这个代码块中,从第11行到第18行并无嵌套,所以编译器可以使用相同的内存来存储两个代码块中不同的变量e。如果想让这两个代码块中的e表示同一个变量,那么你就不应该把它声明为局部变量

  • 第25行的i
    • 链接属性: internal,该属性可以防止它被这个源文件之外的任何函数调用。其他的源文件也可能声明它自己的函数i,但它与这个源文件的i是不同的函数。
    • 存储类型静态存储
    • 作用域从它声明的位置直到这个源文件结束

函数d不可以调用函数i,因为在d之前不存在i的原型。

总结

具有external链接属性的实体在其他语言的术语里称为全局(global)实体,所有源文件中的所有函数均可以访问它。只要变量并非声明于代码块或函数定义内部,它在缺省情况下的链接属性即为external。如果一个变量声明于代码块内部,在它前面添加extern关键字将使它所引用的是全局变量而非局部变量。除了实体的具体定义位置之外,在它的其他声明位置都使用extern关键字。

具有external链接属性的实体总是具有静态存储类型。全局变量在程序开始执行前创建,并在程序整个执行过程中始终存在。从属于函数的局部变量在函数开始执行时创建,在函数执行完毕后销毁,但用于执行函数的机器指令在程序的生命期内一直存在。

局部变量由函数内部使用,不能被其他函数通过名字引用。它在缺省情况下的存储类型为自动,这是基于两个原因:

  1. 当这些变量需要时才为它们分配存储,这样可以减少内存的总需求量。
  2. 在堆栈上为它们分配存储可以有效地实现递归。

如果觉得让变量的值在函数的多次调用中始终保持原先的值非常重要的话,可以修改它的存储类型,把它从自动变量改为静态变量。

变量类型声明的位置是否存在于堆栈作用域如果声明为static
全局所有代码块之外从声明处到文件尾不允许从其他源文件访问
局部代码块起始处整个代码块变量不存储于堆栈中,它的值在程序整个执行器一直保持
形式参数函数头部整个函数不允许

参考

  1. 《C与指针》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值