C语言|关于C语言变量的作用域、链接、存储期及相关知识(C多文件编程基础)

文章目录

  • 作用域
    • 块作用域(block scope)
    • 函数作用域(function scope)
    • 函数原型作用域(function prototype scope)
    • 文件作用域(file scope)
    • 翻译单元和文件(作用域)
  • 链接(linkage)
  • 存储期(Storege Duration)
    • 静态存储期(static storage duration)
    • 线程存储期(thread storage duration)
    • 自动存储期(automatic storage duration)
    • 动态分配存储期(allocated storage duration)
  • 块作用域的静态变量(Static Variables with Block Scope)
  • 外部链接的静态变量(Static Variables with External Linkage)
  • 内部链接的静态变量(Static Variables with Internal Linkage)
  • 多文件(multiple file)

背景:

作为一名新手,最近在用C代码实现某个项目,文件比较多,所以第一次接触到C代码对函数、结构体的封装,有点懵懵的,不知道为什么这么写,研究后发现,竟然是对作用域的理解还不是很清晰!

所以,特意写此博客,帮助理解,内容都来自于C Primer Plus。

并且我的博客删减内容较多,适合个人理解用,如果想看详细讲解,请移步C Primer Plus 第十二章

C语言多文件编程需要了解的基础知识


作用域

块作用域(block scope)

某种程度来说,花括号括起来的都是块。

块作用域变量的可见范围是 从定义处到包含该定义的块的末尾。另外,虽然函数的形式参数声明在函数 的左花括号之前,但是它们也具有块作用域,属于函数体这个块。

函数作用域(function scope)

函数作用域指的是在函数内部定义的局部变量的作用域。这些变量只在函数体内部可见,一旦函数执行完毕,这些变量的生命周期就结束了。函数参数也属于函数作用域。

函数原型作用域(function prototype scope)

函数原型作用域指的是在函数原型中声明的参数名的作用域。这些参数名只在函数原型声明中有效,主要用于文档化目的,以表明函数期望的参数类型。函数的实现可以使用与原型中不同的参数名

Function prototype scope runs from the point the variable is defined to the end of the prototype declaration. What this means is that all the compiler cares about when handling a function prototype argument is the types; the names you use, if any, normally don’t matter, and they needn’t match the names you use in the function definition.

#include <stdio.h>

// 函数原型,其中 a 和 b 的作用域仅限于这个原型
int add(int a, int b);

int main() {
    printf("%d\n", add(2, 3));
    return 0;
}

// 函数实现,可以使用与原型不同的参数名
int add(int x, int y) {
    return x + y;
}

文件作用域(file scope)

file scope variables are also called global variables

#include <stdio.h> 
int units = 0; 
void critic(void); 

int main(void) { 
  ...
}

void critic(void) {
 	...
}

其中units有文件作用域,更准确来说它具有外部链接文件作用域units has file scope with external linkage,下文会对其进行描述)

翻译单元和文件(作用域)

什么是翻译单元(Translation Units)

你认为的多个文件在编译器中可能其实是以一个文件出现。

例如,通常在源代码(.c)中包含一个或多个头文件(.h )。头文件会依次包含其他头文件,所以会包含多个单独的物理文件。但是,C预处理实际上是用包含的头文件内容替换#include指令。所以,编译器源代码文件和所有的头文件都看成是一个包含信息的单独文件。这个文件被称为翻译单元 (translation unit)。

描述一个具有文件作用域的变量时,它的实际可见范围是整个翻译单元。如果程序由多个源代码文件组成,那么该程序也将由多个翻译单元组成。每个翻译单元均对应一个源代码文件和它所包含的文件。

这其实扩大了我们所谓文件作用域的范围

链接(linkage)

A C variable has one of the following linkages: external linkage, internal linkage, or no linkage.

无链接变量:具有块作用域、函数作用域或函数原型作用域的变量,这意味着这些变量属于定义它们的块、函数或原型私有。

内部链接/外部链接变量:具有文件作 用域的变量可以是外部链接或内部链接。

Note Formal and Informal Terms

The C Standard uses “file scope with internal linkage” to describe scope limited to one translation unit (a source code file plus its included header files) and “file scope with external linkage” to describe scope that, at least potentially, extends to other translation units. But programmers don’t always have the time or patience to use those terms. Some common short cuts are to use “file scope” for “file scope with internal linkage” and “global scope” or “program scope” for “file scope with external linkage.”

往往把“内部链接的文件作用域”叫做文件作用域

把“外部链接的文件作用域”叫做全局作用域程序作用域

案例如下:

int giants = 5; 	//file scope, external linkage

static int dodgers = 3; 	//file scope, internal linkage

int main() { ...

} ...

该文件和同一程序的其他文件都可以使用变量giants。而变量dodgers文件私有,该文件中的任意函数都可使用它。

存储期(Storege Duration)

Scope and linkage describe the visibility of identifiers. Storage duration describes the persistence of the objects accessed by these identifiers.

存储器描述了变量的生命周期

静态存储期(static storage duration)

如果对象具有静态存储期,那么它在程序的执行期间一直存在。文件作用域变量具有静态存储期。注意,对于文件作用域变量,关键字 static表明了其链接属性,而非存储期。以 static声明的文件作用域变量具有内部链接。但是无论是内部链接还是外部链接,所有的文件作用域变量都具有静态存储期。

线程存储期(thread storage duration)

用于并发程序设计,程序执行可被分为多个线程。具有线程存储期的对象,从被声明时到线程结束一直存在。以关键字_Thread_local声明一个对象时,每个线程都获得该变量的私有备份。

自动存储期(automatic storage duration)

块作用域的变量通常都具有自动存储期。当程序进入定义这些变量的块时,为这些变量分配内存;当退出这个块时,释放刚才为变量分配的内存。 这种做法相当于把自动变量占用的内存视为一个可重复使用的工作区或暂存区。例如,一个函数调用结束后,其变量占用的内存可用于储存下一个被调用函数的变量

为什么自动呢?就是因为这些对内存的操作都是系统为我们完成的。

不过,

然而,块作用域变量也能具有静态存储期。为了创建这样的变量,要把变量声明在块中,且在声明前面加上关键字static

所以,你能理解自动变量吗?

动态分配存储期(allocated storage duration)

也就是我们熟悉的malloccalloc()reallocfree()

在这里插入图片描述

块作用域的静态变量(Static Variables with Block Scope)

静态变量(static variable)听起来自相矛盾,像是一个不可变的变量。 实际上,静态的意思是该变量在内存中原地不动,并不是说它的值不变。具有文件作用域的变量自动具有(也必须是)静态存储期。前面提到过,可以创建具有静态存储期、块作用域的局部变量。这些变量和自动变量一样,具 有相同的作用域,但是程序离开它们所在的函数后,这些变量不会消失。**也就是说,这种变量具有块作用域、无链接,但是具有静态存储期。**计算机在 多次函数调用之间会记录它们的值。在块中(提供块作用域和无链接)以存储类别说明符static(提供静态存储期)声明这种变量。

#include <stdio.h> void trystat(void);

int main(void) { 
  	int count;
		for (count = 1; count <= 3; count++)
		{
			printf("Here comes iteration %d:\n", count); trystat();]
		}
		return 0;
}

void trystat(void) {
		int fade = 1; 	
  	static int stay = 1;

		printf("fade = %d and stay = %d\n", fade++, stay++);
}

其中变量stay就具有全局作用域

外部链接的静态变量(Static Variables with External Linkage)

外部链接的静态变量具有文件作用域、外部链接和静态存储期。该类别 有时称为外部存储类别(external storage class),属于该类别的变量称为外 部变量(external variable)。把变量的定义性声明(defining declaration)放 在在所有函数的外面便创建了外部变量。当然,为了指出该函数使用了外部 变量,可以在函数中用关键字extern再次声明。如果一个源代码文件使用的 外部变量定义在另一个源代码文件中,则必须用extern在该文件中声明该变 量。

int Errupt; 			//外部定义的变量
double Up[100]; 	//外部定义的数组
extern char Coal;	//如果Coal被定义在另一个文件,则必须这样声明

void next(void); 
int main(void) 
{ 
  extern int Errupt;	//此时external为可选的声明

	extern double Up[]; //此时external为可选的声明
	...
}
void next(void)
{
  ...
}

注意,在main()中声明Up数组时(这是可选的声明)不用指明数组大小,因为第1次声明已经提供了数组大小信息。main()中的两条 extern 声明完全可以省略,因为外部变量具有文件作用域,所以ErruptUp从声明处到文件结尾都可见。它们出现在那里,仅为了说明main()函数要使用这两个变量

内部链接的静态变量(Static Variables with Internal Linkage)

该存储类别的变量具有静态存储期、文件作用域和内部链接。在所有函数外部(这点与外部变量相同),用存储类别说明符static定义的变量具有这种存储类别:

static int svil = 1;
int main (void)
{
	...  
}

就是声明一个静态的全局变量

多文件(multiple file)

只有当程序由多个翻译单元组成时,才体现区别内部链接和外部链接的重要性。

复杂的C程序通常由多个单独的源代码文件组成。有时,这些文件可能要共享一个外部变量。**C通过在一个文件中进行定义式声明,然后在其他文件中进行引用式声明来实现共享。**也就是说,除了一个定义式声明外,其他声明到要使用extern关键字。而且,只有定义式声明才能初始化变量。

注意,如果外部变量定义在一个文件中,那么其他文件在使用该变量之 前必须先声明它(用 extern关键字)。也就是说,在某文件中对外部变量进 行定义式声明只是单方面允许其他文件使用该变量,其他文件在用extern声明之前不能直接使用它

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

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

相关文章

kafka启动报错(kafka.common.InconsistentClusterIdException)

文章目录 前言kafka启动报错(kafka.common.InconsistentClusterIdException)1. 查找日志2. 定位问题/解决 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不…

qt实现方框调整

效果 在四周调整 代码 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QWidget>class MainWindow : public QWidget {Q_OBJECT public:explicit MainWindow(QWidget *parent 0);~MainWindow();void paintEvent(QPaintEvent *event);void updateRect();void re…

ZYNQ--PL读写PS端DDR数据

PL 和PS的高效交互是zynq 7000 soc开发的重中之重,我们常常需要将PL端的大量数 据实时送到PS端处理,或者将PS端处理结果实时送到PL端处理,常规我们会想到使用DMA 的方式来进行,但是各种协议非常麻烦,灵活性也比较差,本节课程讲解如何直接通过AXI总 线来读写PS端ddr的数据…

什么是基尼系数

基尼系数是国际上用来综合考察居民内部收入分配差异状况的一个重要分析指标。每个人的收入有多有少&#xff0c;差距大时&#xff0c;基尼系数就高&#xff1b;差距小时&#xff0c;基尼系数就低。 一、基本概念 基尼系数表示在全部居民收入中&#xff0c;用于进行不平均分配…

补充centos7软件包的方式/编译安装源码包软件/企业案例/linux进程管理/企业管理进程系列命令(企业经验)--8820字详谈

cenros7软件包的安装方式 软件包分类安装方式优缺点rpm包软件开发商编译打包&#xff0c;安装简单&#xff0c;快速软件版本可能偏低&#xff0c;安装路径是固定好的源码包自己手动编译安装并且复杂软件爸爸随意选&#xff0c;可以定制安装路径二进制包解压就可以使用不能进行…

什么是AIGC技术

AIGC技术&#xff0c;即人工智能全局优化控制技术&#xff0c;是一种将人工智能与全局优化控制方法相结合的技术。它的主要目标是通过智能化的方法来解决复杂系统的优化问题&#xff0c;提高系统的性能和效率。 AIGC技术的主要目标是利用人工智能算法和技术来实现对系统整体的…

第三篇:Python编程基础:掌握核心语法与开发技巧

Python编程基础&#xff1a;掌握核心语法与开发技巧 1 引言 在这个信息化迅速蔓延的世界中&#xff0c;Python语言如同钥匙一般开启了通往各种可能性的大门。无论你是数据科学家、网络工程师、机器学习专家&#xff0c;还是仅仅对自动化办公感兴趣的办公室人员&#xff0c;Pyt…

【Elasticsearch<二>✈️✈️】基本属性概念与MySQL数据库的不同之处

目录 &#x1f378;前言 &#x1f37b;一、Elasticsearch 基本属性 1.1 ES VS MySQL 1.2 ES 属性概念 1.3 ES 的增删改查 &#x1f37a;二、自动补全场景 2.1 场景举例 2.2 使用数据分词器 2.3 查询的流程 2.4 整个查询流程图 &#x1f379;章末 &#x1f378;前言 上次初步…

Zynq 7000 系列中的Interconnect(互联)简介

PS&#xff08;处理器子系统&#xff09;内部的互联结构包含了多个交换机&#xff0c;用于通过AXI点对点通道连接系统资源。这些通道负责在主机和从机客户端之间进行地址、数据和响应事务通信。该互联结构负责管理多个待处理的事务&#xff0c;并且为Arm CPU设计了低延迟路径&a…

UE4_动画基础_FootIK

角色由于胶囊体的阻挡&#xff0c;双脚与地面平行&#xff0c;不会与斜坡、台阶等贴合&#xff0c;有一条腿会处于悬空状态&#xff0c;通过双骨骼IK节点使一只脚太高&#xff0c;让后胶囊体下降&#xff0c;修正双脚的角度。这就是逆向运动IK的方法。 一、新建第三人称模板游戏…

OpenStack云计算(十四)——综合演练手动部署OpenStack,

本项目的项目实训可以完全参考教材配套讲解的详细步骤实施&#xff0c;总体来说实训工作量较大&#xff0c;可根据需要选做&#xff0c;重点观看配套的微课视频。 项目实训一 【实训题目】 搭建OpenStack云平台基础环境 【实训目的】 掌握OpenStack基础环境的安装和配置方…

CTFshow-PWN-栈溢出(pwn36)

存在后门函数&#xff0c;如何利用&#xff1f; 好好好&#xff0c;终于到了这种有后门函数的了 checksec 检查一下&#xff1a; 32 位程序&#xff0c;RELRO 保护部分开启 RWX: Has RWX segments 存在可读可写可执行的段 使用 ida32 看 main 函数 跟进 ctfshow 函数…

C++系列-命名空间

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 命名空间 在C/C中&#xff0c;变量&#xff0c;函数和后面要学到的类都是大量存在的&#xff0c;这些变量&#xff0c;函数和类的名称都存在于全局作用域中&#xff0c;可能会导…

电机入门1

文章目录 122.12.22.3 33.13.23.33.4 1 2 2.1 电机板 驱动板电机分类 驱动器分类 转速 转向扭矩定时器 ADC 2.2 PID 自动控制 的核心闭环控制算是 PID的应用 2.3 无刷电机用的 可大大提高其控制效率 和控制精度 3 开发板的IO 电流太小了 20~25ma 电机要A 驱动板 信号放大没舵…

Linux防火墙相关命令以及ip白名单配置

Linux防火墙相关命令以及ip白名单配置 firewall防火墙基础命令查看防火墙的服务状态查看防火墙的状态服务的开启、关闭和重启查看防火墙规则端口的查询、开放和关闭重启防火墙 防火墙白名单配置部分参数介绍 firewall防火墙基础命令 查看防火墙的服务状态 systemctl status f…

乘数而上,创邻科技入选2024数商典型应用场景“乘数榜”

4月18日&#xff0c;由浙江省科学技术协会指导的2024未来数商大会在杭州成功举办。本次大会以“场景突破 乘数而上”为主题&#xff0c;国际国内数商共聚未来科技城学术交流中心&#xff0c;聚焦数据要素市场的制度创新、数据治理、场景应用与生态构建等话题展开研讨。 大会现…

C++入门基础(一)

目录 C关键字命名空间命名冲突例子域的概念理解命名空间定义例子1例子2例子3例子4例子5例子6例子7 C输出与输入输出输入 感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接 &#x1f412;&#x1f412;&#x1f412; 个人主页 &#x1f978;&#x1f978;&#x…

汽车底盘域的学习笔记

前言&#xff1a;底盘域分为传统车型底盘域和新能源车型底盘域&#xff08;新能源系统又可以分为纯电和混动车型&#xff0c;有时间可以再研究一下&#xff09; 1&#xff1a;传统车型底盘域 细分的话可以分为四个子系统 传动系统 行驶系统 转向系统 制动系统 1.1传动系…

计算机体系结构:向量体系结构介绍

向量体系结构介绍 什么是向量&#xff1f; 在计算机体系结构&#xff0c;"向量"&#xff08;vector&#xff09;是指一个由多个相同类型且逻辑上相关的数据元素组成的有序集合。这些元素可以是整数、浮点数、布尔值或其他数据类型&#xff0c;它们在内存中连续存储…

SpringSecurity + Oauth2 + jwt实现单点登录

文章目录 前言一、springsecurity oauth2 redis方式的缺点二、oauth2认证的4种模式的选择三、认证服务器的编写 第一步、创建WebSecurity配置类第二步、创建jwt仓库配置类第三步、创建UserDetailsService类第四步、创建认证服务器配置类 四、测试认证服务器的功能 1.创建Login…
最新文章