C语言KR圣经笔记 4.4作用域规则 4.5头文件 4.6静态变量

 4.4 作用域规则

构成一个 C 程序的函数以及外部变量,不需要全都同时编译;程序的源代码可以放在多个源文件中,并且之前编译好的例程可以从库里面加载。需要关心的问题有:

  • 要怎么写声明,才能使变量在编译期间被正确声明?
  • 要怎么安排声明,才能在程序加载时使程序的所有部分都正确地连接?
  • 要怎么组织声明,才能使只有一份拷贝存在?
  • 外部变量是如何初始化的?

接下来我们把前面的计算器程序重新组织到多个文件中,以探讨这些主题。实际上,计算器程序太小了,不值得拆分,但它可以很好地说明在更大型程序中会出现的问题。

一个名字的作用域,指的是程序中可以使用该名字的部分。对于在函数开头声明的自动变量,其作用域就是该变量所在的函数。不同函数中相同名字的局部变量之间是无关的。对函数参数来说同样此,因为它们实际上也是局部变量。

外部变量或函数的作用域,在所编译的文件中,从它们声明的地方开始,一直延续到文件末尾。例如,如果 main, sp, val,push 和 pop 在同一个文件中定义,而且顺序也和4.3节中给出的相同,即

main() { ... }

int sp = 0;
double val[MAXVAL];

void push(double f) { ... }

double pop(void) { ... }

则变量 sp 和 val 可以用在 push 和 pop 中,只要简单用它们的名字即可,不需要额外的声明。但  sp 和 val 这两个名字在 main 中是不可见的,push 和 pop 同样也对main不可见。

另一方面,如果外部变量在它定义之前要被引用,或者使用外部变量的源文件不是定义它的源文件,则使用之前必须加上 extern 声明。

区分外部变量的定义声明是很重要的。声明宣布了变量的属性(主要是它的类型);定义还为变量分配了内存空间。如果下面两行

int sp;
double val[MAXVAL];

出现在所有函数之外,说明它们定义了外部变量 sp 和 val,分配了内存空间,而且这个定义还能作为声明,供该源文件后面的部分使用。另一方面,下面两行

extern int sp;
extern double val[MAXVAL];

对源文件后面的部分声明: sp 是一个 int 而 val 是一个 double 数组(其大小在其他地方确定),但并没有创建变量,或为它们分配内存空间。

在构成一个程序的所有源文件中,一个外部变量只能有一个定义,其他文件可以包含该变量的 extern 声明,用于访问该变量。(在包含外部变量定义的文件中,也可以存在该变量的 extern 声明。)数组大小必须在定义中指定,但在 extern 声明中是可选的。

外部变量的初始化,只能跟着定义一起。

函数 push 和 pop 可以在一个文件中定义,而变量 val 和 sp 在另一个文件中定义并初始化。然后有必要把这些定义和声明绑定在一起:(实际上不太可能这样组织程序,这里只是为了说明)

文件1:

        extern int sp;

        extern double val[];

        

        void push(double f) { ... }

        

        double pop(void) { ... }

        

文件2:

        int sp = 0;

        double val[MAXVAL];

由于文件1中的 extern 声明在所有函数定义的前面,也都在它们的外面,故它们能用于所有函数;这一套声明对文件1完全足够了。如果在一个文件(文件2)中,先使用了 sp 和 val ,之后才定义了  sp 和 val ,那么也需要在文件前面加上这套声明。

4.5 头文件

现在让我们来考虑下,如果计算器程序的各个组成部分都大幅增加,怎么将它分到多个源文件中。main 函数会在一个文件中,我们称之为 main.c; posh、pop 及其变量放到第二个文件中,叫 stack.c;getop 放到第三个文件中,叫 getop.c。最后, getch 和 ungetch 放到第四个文件中,叫 getch.c;我们将它们与其余部分隔开,是因为在实际的程序中,它们可能来自单独编译的库【而不是和其他部分一起编译】

还有一件事需要担心——文件之间共享的定义和声明。我们想将其尽可能地集中化,这样就只需要写好一份拷贝,而当程序进化时也只需要保证一份拷贝正确就够了。因此,我们将把这个公共资料放在一个头文件里面,叫做 calc.h,有需要就引入。(#include 行在第4.11节描述。)结果程序就像这样:

在“让每个文件只访问它所需的信息”的渴望与“越多头文件越难维护”的现实之间,两者有个权衡。在程序达到一定规模之前,最好的方式可能就是用一个头文件包含程序中所有任意两部分之间需要共享的所有内容;本节我们就是这么做的。对一个更大的程序来说,代码需要更多的组织和更多的头文件。

4.6 静态变量

stack.c 中的变量 sp 和 val,以及 getch.c 中的 buf 和 bufp,是它们各自所在源文件中的函数私有的,并不想要被程序其他部分访问。用于外部变量或函数的 static 静态声明,把对象的作用域限制到所编译源文件的剩余部分。这样,外部的 static 就提供了一种方式来隐藏 getch-ungetch 组合中像 buf 和 bufp 这样的名字:它们必须是外部的,这样才能被共享,然而对 getch 和 ungetch 的用户却不可见。

静态存储通过在正常的声明之前加上 static 前缀来指定。如果两个例程和两个变量在一个文件内编译,如

static char buf[BUFSIZE]    /* ungetch的缓存 */
static int bufp = 0;        /* buf的下一个空闲位置 */

int getch(void) { ... }

void ungetch(int c) { ... }

则没有其他例程能够访问 buf 和 bufp,而且它们的名字不会与同一程序内其他文件中相同的名字冲突。以同样的方式,也能把 push 和 pop 用来做栈操作的变量 sp 和 val 隐藏起来,即声明 sp 和 val 为 static。

外部的 static 声明最常用于变量,但它也能用于函数。通常,函数名是全局的,对整个程序的任何部分都可见。然而,如果一个函数被声明为 static,它的名字在它声明的文件之外是不可见的。

static 声明也能用于内部变量。内部的 static 变量对于其所在的函数是局部的,这点正如自动变量一样,但和自动变量不一样的是,即使函数没有被激活的时候(函数激活即指每次进入和离开函数),内部 static 变量也是存在的。这意味着,内部的 static 变量在一个函数内提供了私有的,永久的存储空间。

练习4-11、修改 getop,使之不需要使用 ungetch。提示:使用内部的 static 变量。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/255580.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网络编程『socket套接字 ‖ 简易UDP网络程序』

🔭个人主页: 北 海 🛜所属专栏: Linux学习之旅、神奇的网络世界 💻操作环境: CentOS 7.6 阿里云远程服务器 文章目录 🌤️前言🌦️正文1.预备知识1.1.IP地址1.2.端口号1.3.端口号与进…

三菱plc学习入门(一,认识三菱plc)

今天就开始对三菱的plc软件入一个门,希望小编的文章对读者和初学者有所帮助!欢迎评论指正,废话不多说,下面开始学习。 目录 plc的型号介绍 M表示什么? T表示什么? R表示什么? 为什么三菱没…

Pytorch nn.Linear()的基本用法与原理详解及全连接层简介

主要引用参考: https://blog.csdn.net/zhaohongfei_358/article/details/122797190 https://blog.csdn.net/weixin_43135178/article/details/118735850 nn.Linear的基本定义 nn.Linear定义一个神经网络的线性层,方法签名如下: torch.nn.Li…

得帆信息创始人-张桐,受邀出席 BV百度风投AIGC主题论坛

近日,得帆信息创始人兼CEO张桐,作为百度风投被投代表企业创始人受邀出席“向未来,共成长” BV百度风投AIGC主题论坛。 与包括上海市徐汇区相关部门领导、百度集团相关事业部负责人及代表,以及来自国寿资本、中网投、麦顿投资的投资…

install cuda cudnn tersorRT

# 安装 $ ubuntu-drivers devices$ sudo apt-get install nvidia-driver-470-server # 推荐是server,都可以。#delelt sudo apt --purge remove nvidia-* CUDA Toolkit Archive | NVIDIA Developerhttps://developer.nvidia.com/cuda-toolkit-archive CUDA Toolk…

【计算机网络】TCP协议——1.报文格式详解

前言 上篇讲解了UDP报文格式。TCP和UDP是同层协议,都属于传输层,数据来源于上层——应用层 目录 一. TCP协议概述 二. TCP报文格式 1. 两个问题 2. 确认号和序列号 3. 标志位字段 4. 窗口大小 5. 校验和字段 6. 紧急指针与紧急数据 7. 选项字…

后端低代码平台探索总结

业务需求快速变化的背景 我们在对业务需求进行梳理后,在进行程序设计时,对于将来可能发生变化的常量、变量、阀值、开关、条件、公式等等,可能会配置在环境变量或数字字典来支持可配置。但是需求变化往往会更加复杂、更加不可预测&#xff0…

PDF控件Spire.PDF for .NET【转换】演示:将多个图像转换为单个 PDF

如果您想要将多个图像合并为一个文件以便于分发或存储,将它们转换为单个 PDF 文档是一个很好的解决方案。这一过程不仅节省空间,还确保所有图像都保存在一个文件中,方便共享或传输。在本文中,您将学习如何使用Spire.PDF for .NET …

VuePress安装及使用

目录 前言一、依赖环境二、vuepress 安装和使用1.初始化2.将 VuePress 安装为本地依赖3. package.json 中添加脚本4. 新建 docs 文件夹5.启动6. 效果 三、进阶使用1.新增配置文件2.安装搜索插件3.config.js 中增加配置4.效果展示5.注意 前言 VuePress 是一个以 Markdown 为中心…

Vue中英文翻译小结

背景:时局艰难,后端开发被强制写了vue,这不有个需求是中英文翻译,特此记录下,该怎么个翻译法子。 先引入全局的路由国际化文件,zh.js 和 en.js 1.关于插值表达Button里面 {{ $t(reinsop.common.back) }} …

软件测试技术分享| Appium用例录制

下载及安装 下载地址: github.com/appium/appi… 下载对应系统的 Appium 版本,安装完成之后,点击 “Start Server”,就启动了 Appium Server。 在启动成功页面点击右上角的放大镜,进入到创建 Session 页面。配置好…

❀My虚拟机上的ftp服务器搭建(centos)❀

❀My虚拟机上的ftp服务器搭建(centos)❀ 在CentOS上搭建FTP服务器可以使用vsftpd软件,下面是详细的搭建教程: ①安装vsftpd软件 在终端中输入以下命令进行安装: sudo yum install vsftpd ②配置vsftpd 打开vsftpd的配置文件,…

Apache Flume(5):多个agent模型

可以将多个Flume agent 程序连接在一起,其中一个agent的sink将数据发送到另一个agent的source。Avro文件格式是使用Flume通过网络发送数据的标准方法。 从多个Web服务器收集日志,发送到一个或多个集中处理的agent,之后再发往日志存储中心&…

互联网加竞赛 python+opencv+深度学习实现二维码识别

0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 pythonopencv深度学习实现二维码识别 🥇学长这里给一个题目综合评分(每项满分5分) 难度系数:3分工作量:3分创新点:3分 该项目较为新颖&…

智能优化算法应用:基于算术优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用:基于算术优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于算术优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.算术优化算法4.实验参数设定5.算法结果6.…

信号与系统分析导论——“信号与系统”

小雅兰马上就要期末考试啦,最近也要开始准备期末复习了,下面,就让我们进入信号与系统分析导论的世界吧!!!! 信号(signal) 系统(system) 信号的描…

竞赛保研 python的搜索引擎系统设计与实现

0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 python的搜索引擎系统设计与实现 🥇学长这里给一个题目综合评分(每项满分5分) 难度系数:3分工作量:5分创新点:3分 该项目较为新颖&#xff…

LLMs 玩狼人杀:清华大学验证大模型参与复杂交流博弈游戏的能力

作者:彬彬 编辑:李宝珠,三羊 清华大学研究团队提出了一种用于交流游戏的框架,展示了大语言模型从经验中学习的能力,还发现大语言模型具有非预编程的策略行为,如信任、对抗、伪装和领导力。 近年来&#x…

SpringBoot基于gRPC进行RPC调用

SpringBoot基于gRPC进行RPC调用 一、gRPC1.1 什么是gRPC?1.2 如何编写proto1.3 数据类型及对应关系1.4 枚举1.5 数组1.6 map类型1.7 嵌套对象 二、SpringBoot gRPC2.1 工程目录2.2 jrpc-api2.2.1 引入gRPC依赖2.2.2 编写 .proto 文件2.2.3 使用插件机制生产proto相关…

Xxl-job-admin 数据库使用DM8/达梦改造

Xxl-job 简介 XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。 XXL-JOB-ADMIN 是针对分布式定时任务管理的Web管理平台,默认使用的数据库是MySQL 8版本。 业务背景 在项目中使用分布式定时任务调度框架:xxl-…