C学习-预处理指令-static和extern关键字-对函数、变量的做用(七)

咱们知道,不一样类型的变量有不一样的存储类型、不一样的生命周期、不一样的做用域。这讲介绍2个比较重要的关键字:staticextern。static和extern不只能够用在变量上,还能够用在函数上。html

1、变量类型

C语言根据变量的存储类型的不一样,能够把变量分为:自动变量静态变量寄存器变量`。
自动变量是存储在堆栈中的。ios

静态变量是存储在静态内存中的,也就是不属于堆栈。模块化

哪些是静态变量:函数

  • 全部的全局变量都是静态变量spa

  • 被关键字static修饰的局部变量也是静态变量code

生命周期:静态变量在程序运行以前建立,在程序的整个运行期间始终存在,直到程序结束。htm

2、extern与函数

咱们知道:若是一个程序中有多个源文件(.c),编译成功会生成对应的多个目标文件(.obj),这些目标文件还不能单独运行,由于这些目标文件之间可能会有关联,好比a.obj可能会调用c.obj中定义的一个函数。将这些相关联的目标文件连接在一块儿后才能生成可执行文件。blog

先来理解2个概念:生命周期

外部函数:若是在当前文件中定义的函数容许其余文件访问、调用,就称为外部函数。C语言规定,不容许有同名的外部函数。ip

内部函数:若是在当前文件中定义的函数不容许其余文件访问、调用,只能在内部使用,就称为内部函数。C语言规定不一样的源文件能够有同名的内部函数,而且互不干扰。

接下来就演示在一个源文件中调用另一个源文件定义的函数,好比在main.c中调用one.c中定义的one函数。

1.首先在one.c中定义了一个one函数

若是你想让这个one函数能够被main.c访问,那么one函数就必须是外部函数。完整的定义是要加上extern关键字。

clipboard.png

不过这个extern跟auto关键字同样废,彻底能够省略,由于默认状况下,全部的函数就是外部函数。咱们能够简化一下:

clipboard.png

2.接下来,我想在main.c的main函数中,调用one.c中的one函数

clipboard.png

怎样才能调用one.c中的one函数呢?你可能会产生2个想法:

想法1:直接在main函数中写上one();

clipboard.png

这个作法确定不行,由于main函数根本不知道one函数的存在,怎么调用呢?这个在标准C编译器里面会报错的,可是在Xcode中只是个警告。

想法2:在main.c中包含one.c文件

clipboard.png

你们都知道#include的做用纯粹就是内容拷贝,因此又至关于

clipboard.png
哎,这么一看好像是对的哦,在main函数前面定义了个one函数,而后在main函数中调用了这个one函数。从语法上看是对的,因此编译是没问题的。可是这个程序不可能运行成功,由于在连接的时候会报错。咱们已经在one.c中定义了one函数,如今又在main.c中定义one函数,C语言规定不容许有同名的外部函数,连接的时候连接器会发如今one.obj和main.obj中定义了同一个函数,会直接报错,Xcode中的错误信息是这样的:

clipboard.png

duplicate symbol _one是说one这个标识符重复了,linker是指连接器。

上面的2种想法都是不可行的,其实思路是一致的:让main函数知道one函数的存在正确的作法应该是在main函数前面对one函数进行提早声明(看清楚,是声明,不是定义,定义和声明是两码事)

3.在main函数前面对one函数进行提早声明

你想要把其余源文件中定义的外部函数拿过来声明,完整的作法,应该使用extern关键字,表示引用别人的"外部函数"

clipboard.png

运行程序,从控制台输出能够发现 "one.c中定义的one函数" 已经被 "main.c的main函数" 成功调用了。

也有人可能会立刻冒出一个想法:假如除开one.c,还有其余源文件也有定义这个one函数怎么办?那main函数调用的到底是谁的one函数啊?放心,绝对不会有这种状况,刚才不是说了么,不容许重复定义同一个外部函数,否则连接器会报错的,因此只会有一个外部one函数。

上述就是extern关键字对函数的做用:用来定义和声明一个外部函数。其实extern又跟auto同样废,彻底能够省略。因而,咱们能够简化成这样:

clipboard.png

为了模块化地开发,在正规的项目里面,咱们会把one函数的声明写到另外一个头文件中,固然,这个头文件的命名最好有意义、规范一点,好比叫one.h。之后,谁想调用这个one函数,包含one.h这个头文件就好了。因而最后的代码结构是这样的:

clipboard.png

clipboard.png

clipboard.png

3、static与函数

1.定义内部函数

从上面的例子能够看出,one.c中定义的one函数是能够被其余源文件访问的。其实有时候,咱们可能想定义一个"内部函数",也就是不想让其余文件访问本文件中定义的函数。这个很是简单,你只须要在定义函数的时候加个static关键字便可。

(咱们就在上面例子的代码基础上进行修改)

clipboard.png

我在void one()的前面加了个static,表明one函数是个内部函数。

而后你会发现程序运行不起来了,在连接的时候就报错了。报错的缘由很简单:咱们在main.c中调用了one.c中定义的one函数,可是如今one.c的one函数是个"内部函数",不容许其余文件访问。咱们来看看错误信息:

clipboard.png

第1个红框中的Undefined symbols...意思是one这个标识符没有被定义,也就是找不到one;第2个红框的linker代表是连接器报错了。

但这个程序是能够编译成功的,由于咱们在main函数前面声明了one函数(函数的声明和定义是两码事),这个函数声明能够理解为:在语法上,骗一下main函数,告诉它one函数是存在的,因此从语法的角度上main函数是能够调用one函数的。究竟这个one函数存不存在呢,有没有被定义呢?编译器是无论的。在编译阶段,编译器只会检测单个源文件的语法合不合理,并不检测函数有没有定义,只有在连接的时候才会检测这个函数存不存在,也就是有没有被定义。

clipboard.png

clipboard.png

咱们再来讨论一个问题,为何好多状况下都是能够成功编译,可是连接的时候报错呢?只要你理解编译和连接的做用就好办了。

所谓编译就是单独检查每一个源文件的语法是否合理,并不会检查每一个源文件之间的关联关系,一个源文件编译成功就生成一个目标文件

所谓连接就是检查目标文件的关联关系,将相关联的目标文件组合在一块儿,生成可执行文件

看完这2个概念,再回去思考下前面报的错,应该能够彻底明白了。

2.声明内部函数

咱们还能够用static声明一个内部函数

1 #include <stdio.h>
 2 
 3 static void test();
 4 
 5 int main(int argc, const char * argv[])
 6 {
 7     test();
 8     return 0;
 9 }
10 
11 static void test() {
12     printf("调用了test函数");
13 }

在第11行定义了一个test函数,这是一个内部函数,接着在第3行对test函数进行提早声明,而后就能够在第7行能够调用test()函数了

4、static、extern与函数的总结

1.static

在定义函数时,在函数的最左边加上static能够把该函数声明为内部函数(又叫静态函数),这样该函数就只能在其定义所在的文件中使用。若是在不一样的文件中有同名的内部函数,则互不干扰。

static也能够用来声明一个内部函数

2.extern

定义函数时,若是在函数的最左边加上关键字extern,则表示此函数是外部函数,可供其余文件调用。C语言规定,若是在定义函数时省略extern,则隐含为外部函数。

  • 在一个文件中要调用其余文件中的外部函数,则须要在当前文件中用extern声明外部函数,而后就可使用,这里的extern也能够省略。

5、对变量的做用

extern 关键字对变量的做用

一、不一样源文件中的同名变量

前面讲到,你在一个源文件中不管写多少遍全局变量int a;,它们表明的都是同一个变量。还有一个事实,假如在另外一个源文件中也有全局变量int a;,那么这两个源文件的全部全局变量int a;都表明着同一个变量。extern关键字仍是适用的,好比:

clipboard.png
clipboard.png

或者是:

clipboard.png
clipboard.png

上面的两种状况下,test.c和main.c中使用的全局变量a都仍是表明着同一个变量

注意了,不能够两个文件的全部所有变量a都用extern,下面的作法是错误的:

clipboard.png
clipboard.png

由于extern是用来声明一个已经定义过的变量这两个文件都是在声明变量,没有人定义变量,在连接的时候确定报错

clipboard.png

大体错误意思是:标示符a未定义

static 关键字对变量的做用

但不少时候,咱们并不想让源文件中的全局变量跟其余源文件共享,至关于私有的全局变量,那么你就得用static关键字来定义变量。

clipboard.png

clipboard.png

这样写完,test.c和main.c的变量a分别表明着不一样的变量,它们是没有联系的、互不干扰的。也就是说,main.c没法访问test.c中的变量a,所以在main.c中将a修改成10后,test.c中的a依然为0。输出结果:
clipboard.png

其实static还能够用来修饰局部变量。

static和extern的总结

1.extern能够用来声明一个全局变量,可是不能用来定义变量

2.默认状况下,一个全局变量是能够供多个源文件共享的,也就说,多个源文件中同名的全局变量都表明着同一个变量

3.若是在定义全局变量的时候加上static关键字,此时static的做用在于限制该全局变量的做用域,只能在定义该全局变量的文件中才能使用,跟其余源文件中的同名变量互不干扰


注:本文转自李明杰老师的博文

相关文章
相关标签/搜索