【C语言】详解static和extern关键字

文章目录

  • [1. 前言](#1. 前言)
  • [2. 作用域和生命周期](#2. 作用域和生命周期)
    • [2.1 作用域](#2.1 作用域)
      • [2.1.1 全局变量和局部变量](#2.1.1 全局变量和局部变量)
    • [2.2 生命周期](#2.2 生命周期)
  • [3. static关键字](#3. static关键字)
    • [3.1 static修饰的局部变量](#3.1 static修饰的局部变量)
  • [4. extern关键字](#4. extern关键字)
  • [5. extern和static关键字的相互作用](#5. extern和static关键字的相互作用)
    • [5.1 static修饰函数](#5.1 static修饰函数)
  • 6.总结

1. 前言

可能在你遇到这篇文章之前,你可能并未听过C语言中还有staticextern这两个关键字。或者你也许听过但是并未使用过。

这些都不是问题,在本文中,我将细细的给大家揭晓这两个关键字背后的秘密,及以后我们该如何使用它们。🥇🥇😊

2. 作用域和生命周期

再讲staticextern这两个关键字之前,有必要先给大家讲一下变量的作用域和生命周期。

2.1 作用域

作用域是个什么东东?

顾名思义,作用域就是变量能够作用的范围。换句话说,就是变量能够在这个范围内生效,出了这个范围之后,变量就不起作用了。
这就又引发出一个问题:这个所谓的"范围"在代码中如何界定?

界定的方法:是以一个 {} 为 标志,每一个{}就代表着一个作用域。

听到这里,你可能还是一头雾水。下面我就给几个例子,让你感受到作用域带来的魅力!

我们稍微再修改一下(就是在变量a后面加一个花括号并在里面写一点内容):

那如果我们在图示花括号后面,要打印一个变量b的值,程序还能正确运行吗?

可以看到,编译器给我们报的错误:"b"未声明的标识符。这也就说明了,这个范围已经超出了变量b的作用域了,因此编译器才无法识别出变量b。

通过上述的例子,我相信大家已经对作用域的概念已经深刻的理解了。

2.1.1 全局变量和局部变量

既然都讲到了作用域,那就必须得聊一下全局变量和局部变量了。

变量按照作用域进行分类,可以分为全局变量和局部变量。

全局变量:拥有整个项目工程的作用域。换句话说,就是它能够在整个代码文件中都有效。全局变量是存储在内存的静态区。

局部变量:拥有在该范围内的作用域,出了这个范围该变量就失去作用了。局部变量是存储在内存的栈区的。

注意:全局变量应尽量少地在编程的过程中使用。因为,全局变量所带来地不可控的因素太多了,比较容易错误的使用。

那我们该如何在代码中,识别出哪个变量是全局变量还是局部变量呢?

很简单,仔细观察上面的代码,可以看到,全局变量是写在main函数外部的。

所以,我们可以由此得到一个结论:全局变量是出现在main函数的外部的,局部变量是出现在函数内部(包括自定义函数、库函数等)的变量。

另外,再向大家声明一个点:

在我们初步学习变量的命名规则时,里面规定:变量的命名是不能重复的。这里我拓展一个点就是,此条件是在同一个作用域下才生效。

可能有的读者已经get到我的点了。

你不是说这个条件是在同一个作用域下才生效的麻,也就说明了在不同作用域下我可以使用相同的变量名。

那么,如果我全局变量和局部变量都用同一个变量名时,编译器又该如何接招呢

答案是:局部变量优先。换句话说,在局部变量所处的作用域中,用该局部变量的值,而不是全局变量。

下面我就写一段代码,验证我们的猜想:

c 复制代码
#include<stdio.h>
int a = 88;

int main()
{
	int a = 66;
	printf("%d\n", a); //打印a的值为66
	return 0;
}

结果是:

可以看到,打印出来的值是66,而不是88。这就正是验证了我们的猜想是正确的。

2.2 生命周期

所谓的生命周期就更容易理解了,它就是:

⽣命周期指的是变量的创建(申请内存)到变量的销毁(收回内存)之间的⼀个时间段。

  1. 局部变量的⽣命周期是:进⼊作⽤域变量创建,⽣命周期开始,出作⽤域⽣命周期结束。
  2. 全局变量的⽣命周期是:整个程序的⽣命周期。

3. static关键字

static关键字修饰的变量我们称它为静态变量

static关键字的作用:改变变量的生命周期,使之拥有全局变量的生命周期。(但是注意一点,这样的操作并未改变变量的作用域)

那我们说的到底对不对,下面我就写代码,来探讨一下这个问题:

3.1 static修饰的局部变量

可以看到,含有static修饰变量似乎有种记忆能力,它给我们的一种感觉就是能够在之前操作的结果基础上继续进行操作。

对⽐代码1和代码2的效果,你就能理解 static 修饰局部变量的意义。

  • 代码1的test函数中的局部变量i是每次进⼊test函数先创建变量(⽣命周期开始)并赋值为0,然后
    ++,再打印,出函数的时候变量⽣命周期将要结束(释放内存)。
  • 代码2中,我们从输出结果来看,i的值有累加的效果,其实 test函数中的i创建好后,出函数的时候是
    不会销毁的,重新进⼊函数也就不会重新创建变量,直接上次累积的数值继续计算。

使用建议 :未来⼀个变量出了函数后,我们还想保留值,等下次进⼊函数继续使⽤,就可以使⽤static

修饰。

4. extern关键字

extern关键字的作用:赋予变量外部链接属性。

什么是外部链接?

这个问题通常会出现在这么一个场景中,就是我们在大型程序编写中,我们都会将程序的代码划分为多个.c文件,这样做的目的不仅可以增加代码的可读性,还可以提高团队的编程效率。那我们现在假设有个A文件和B文件,A文件的内容和B文件的内容是不能相互使用(相信这个点,大家在编程时都知道),如果强行使用,编译器就会报出错误。

那么现在我接受到了一个任务:我必须要在文件B中使用文件A的内容(一些变量的值、函数等等)。

那我们该怎么做?

用extern关键字。

在展示代码之前,想跟大家聊一聊,为什么就是用这个关键字?

对于文件A来说,文件B是属于外部文件。那么对于文件B来说,文件A里面的内容就属于外部内容。试想一下,两个毫不相干的东西想要相互使用双方的内容,那就必须得建立一个桥梁(链接)。而这个桥梁能够让文件A能够使用文件B的内容,建立桥梁的操作就是在建立外部链接属性。extern在这方面就是专业对口的。

在下面就举个例子:

首先在用这个关键字时,至少得创建两个或两个以上的.c文件。在本例中,创建两个.c文件。

在test1.c的内容:

c 复制代码
int a = 66;

在test.c的内容:

c 复制代码
#include<stdio.h>
extern int a;//这里的变量名必须与test1.c中你想引用的变量名得是一致的
int main()
{
	printf("a = %d\n", a);
	return 0;
}

5. extern和static关键字的相互作用

上面的例子如果你看懂的话,那就太好了。我们继续突发奇想地改造上述的代码。

我们这样改造:

将test1.c中的变量变为静态变量

c 复制代码
static int a = 66;

再编译会出现什么情况?

😲😲😲既然改了一下代码就报错了,这是什么原因呢?

就是static和extern关键字相互作用的结果。

结论:⼀个全局变量被static修饰,使得这个全局变量只能在本源⽂件内使⽤,不能在其他源⽂件内使⽤。
本质原因是全局变量默认是具有外部链接属性的,在外部的⽂件中想使⽤,只要适当的声明就可以使
⽤;但是全局变量被 static 修饰之后,外部链接属性就变成了内部链接属性,只能在⾃⼰所在的源
⽂件内部使⽤了,其他源⽂件,即使声明了,也是⽆法正常使⽤的。

使⽤建议:如果⼀个全局变量,只想在所在的源⽂件内部使⽤,不想被其他⽂件发现,就可以使⽤

static修饰。

5.1 static修饰函数

相信初学C语言的读者们可能都没有看过这种写法,static竟然还能修饰函数。

情况一:
在test1.c中的内容:

c 复制代码
int Add(int x, int y)
{
	return x + y;
}

在test.c中的内容:

c 复制代码
#include<sdtio.h>
extern int Add(int x, int y);
int main()
{
	printf("%d\n",Add(2,3));
	return 0;
}

注意,在Add函数形参这部分,可以不用专门写x和y,只写类型也可以。

情况二:

在test1.c内容中:

c 复制代码
static int Add(int x, int y)
{
	return x + y;
}

在test.c内容中:

c 复制代码
#include<sdtio.h>
extern int Add(int x, int y);
int main()
{
	printf("%d\n",Add(2,3));
	return 0;
}

再看看和之前的代码有什么不同?

发现代码直接报错了。

解释:
其实 static 修饰函数和 static 修饰全局变量是⼀模⼀样的,⼀个函数在整个工程都可以使用,
被static修饰后,只能在本⽂件内部使用,其他文件无法正常的链接使用了。

本质是因为函数默认是具有外部链接属性,具有外部链接属性,使得函数在整个工程中只要适当的声
明就可以被使用。但是被 static 修饰后变成了内部链接属性,使得函数只能在自己所在源文件内部
使用。

使⽤建议 :⼀个函数只想在所在的源⽂件内部使⽤,不想被其他源⽂件使⽤,就可以使⽤ static 修

饰。

6.总结

在本文中,介绍了变量的作用域和生命周期,还说明了static和extern两个关键字以及它们如何使用。

希望能帮到大家。

文章创作不易,如果你觉得写得好不错的话,请不要吝啬你们的点赞和收藏哦!!!🙏🙏🙏

相关推荐
weisian1511 分钟前
Redis篇--常见问题篇6--缓存一致性1(Mysql和Redis缓存一致,更新数据库删除缓存策略)
数据库·redis·缓存
我曾经是个程序员10 分钟前
C#Directory类文件夹基本操作大全
服务器·开发语言·c#
白云~️11 分钟前
uniappX 移动端单行/多行文字隐藏显示省略号
开发语言·前端·javascript
编码浪子17 分钟前
构建一个rust生产应用读书笔记7-确认邮件2
开发语言·后端·rust
天之涯上上32 分钟前
JAVA开发 在 Spring Boot 中集成 Swagger
java·开发语言·spring boot
2402_8575834934 分钟前
“协同过滤技术实战”:网上书城系统的设计与实现
java·开发语言·vue.js·科技·mfc
爱学习的白杨树40 分钟前
MyBatis的一级、二级缓存
java·开发语言·spring
OTWOL1 小时前
两道数组有关的OJ练习题
c语言·开发语言·数据结构·c++·算法
问道飞鱼1 小时前
【前端知识】强大的js动画组件anime.js
开发语言·前端·javascript·anime.js
拓端研究室1 小时前
R基于贝叶斯加法回归树BART、MCMC的DLNM分布滞后非线性模型分析母婴PM2.5暴露与出生体重数据及GAM模型对比、关键窗口识别
android·开发语言·kotlin