持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第24天,点击查看活动详情
static& extern
不考虑内存排布问题(或许之后我会给大家讲到),仅考虑功能和使用上的差异
二者同作为C语言的保留关键字,在使用上是截然相反的两个概念。在这里为大家做一个简单的总结。
static
static修饰的静态变量
在程序中的变量根据作用域可以分为全局变量和局部变量。static既能声明变量,又能对变量进行初始化。如:
static int a; //√
static int a = 10;//√
对于static修饰的静态全局变量来说,其作用域仅在当前C源程序内部。
Q:看到这句话可能有小伙伴要问了,那我在a.c里面写一个非static的全局变量,b.c不也访问不到吗?那么静态全局变量的意义是什么呢?
A:因为通过extern在b.c中声明一个同名的变量,是可以访问到a.c中的非static的全局变量的。但是如果a.c中的全局变量是static的,那么无论b.c通过任何方法都无法访问到。
对于static修饰的局部变量来说,作用域仅在当前代码段。
静态局部变量在使用的周期中,只会被定义和初始化一次(注意是初始化,初始化后可以任意修改值了,不经过初始化默认为0)。每次调用这段代码段时,静态局部变量都会保持最近一次修改后的值。
static修饰函数
对普通的函数而言,只需要包含了定义该函数的头文件即可使用该函数。
但是对于static的函数而言,一旦声明了这个函数是静态的,则它不会被其他任何源文件调用,只能在当前源文件中使用,即使其他源文件包含了函数定义的头文件。这大大保护了函数的安全性,让我们的部分函数不暴露给外界。
extern
extern声明变量
注意这里的字眼,对于static我们使用修饰,而对于extern只能使用声明,因为extern不能对变量进行初始化。
extern int a;//√
extern int a = 10;//x
而使用extern声明了的变量可以在其他任何源文件之中进行调用。虽然能很快捷地共享一部分变量,但是同时也会造成安全性的问题。当你不经意使用了extern,可能就会在另外一个文件的某个地方收获到一个“奇怪”的值。所以对于小型项目的开发,extern变量能帮我们减少一些工作,但是对于大型的项目,我们仍然推荐将要被共享的变量声明在头文件中,作为全局变量被包含。
extern声明函数
用来表示函数已经在其他文件中声明,在这里可以使用。
对于一个其他文件中的非static的函数,我们通常有两种方式使用它:
- 添加该函数声明的头文件,缺点是会加载头文件中的所有函数,导致编译效率不高
- 使用extern修饰想要被全局访问的函数,编译速度快,但是安全性不高
事实上,虽然extern带来的开销更小,但是我们在C的底层开发中通常不会使用extern。因为我们始终不希望一个变量或者函数是暴露在外的,所以extern的使用通常出现在小型工具的使用中来共享几个关键的函数和变量。
总结
| 全局变量 | 局部变量 | 函数 | |
|---|---|---|---|
| static | 作用域仅在当前c文件内部 | 作用域仅在当前代码段。 | 不能被其他源文件调用 |
| extern | 只能进行声明但不能进行定义,使作用域不受限制,可以被多个源文件访问 | 只能进行声明但不能进行定义,使作用域不受限制,可以被多个源文件访问 | 调用其他源文件中的非static的函数 |
总结一下,事实上static的使用是比extern稍微广泛一点的,因为static是安全的。而extern的使用通常是为了提高效率而对经常被重复调用的内容进行共享。二者从意思上就是截然相反的,无论变量或者函数都不能既是static的又是extern的。
最后补充一点,大家学习函数之后,就不要把所有的功能都塞在main函数里执行,尽量把不同功能的代码块封装为函数。如果涉及多文件编译,注意使用static和extern去对内容进行修饰,或者包含有共同声明的头文件。