C语言中static关键字和extern关键字

一.static关键字

1.先来介绍它的第一条也是最重要的一条:隐藏。被static修饰的全局变量和函数,仅在当前文件可见。

当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。为理解这句话,我举例来说明。我们要同时编译两个源文件,一个是a.c,另一个是main.c。

下面是a.c的内容

char a = 'A'; // global variable
void msg() 
{
    printf("Hello\n"); 
}

下面是main.c的内容

int main(void)
{    
    extern char a;    // extern variable must be declared before use
    printf("%c ", a);
    (void)msg();
    return 0;
}

你可能会问:为什么在a.c中定义的全局变量a和函数msg能在main.c中使用?前面说过,所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。此例中,a是全局变量,msg是函数,并且都没有加static前缀,因此对于另外的源文件main.c是可见的。

如果加了static,就会对其它源文件隐藏。例如在a和msg的定义前加上static,main.c就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。Static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏,而对于变量,static还有下面两个作用。


2.修饰全局变量和局部变量

(1)局部变量
普通局部变量是再熟悉不过的变量了,在任何一个函数内部定义的变量(不加static修饰符)都属于这个范畴。编译器一般不对普通局部变量进行初始化,也就是说它的值在初始时是不确定的,除非对其显式赋值。

普通局部变量存储于进程栈空间,使用完毕会立即释放。

静态局部变量使用static修饰符定义,即使在声明时未赋初值,编译器也会把它初始化为0。且静态局部变量存储于进程的全局数据区,即使函数返回,它的值也会保持不变。

变量在全局数据区分配内存空间;编译器自动对其初始化
其作用域为局部作用域,当定义它的函数结束时,其作用域随之结束

小程序体会一下静态局部变量的威力:

#include <stdio.h>

void test()
{
    
static int num = 0;
    num++;
    printf("%d ", num);
    printf("\n");
}
int main()
{
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        test();
    }
    return 0;
}

可见,静态局部变量的效果跟全局变量有一拼,但是位于函数体内部,就极有利于程序的模块化了。

(2)全局变量

全局变量定义在函数体外部,在全局数据区分配存储空间,且编译器会自动对其初始化。普通全局变量对整个工程可见,其他文件可以使用extern外部声明后直接使用。也就是说其他文件不能再定义一个与其相同名字的变量了(否则编译器会认为它们是同一个变量)。静态全局变量仅对当前文件可见,其他文件不可访问,其他文件可以定义与其同名的变量,两者互不影响。在定义不需要与其他文件共享的全局变量时,加上static关键字能够有效地降低程序模块之间的耦合,避免不同文件同名变量的冲突,且不会误使用。

3.static的第三个作用是默认初始化为0,但是可以赋值

其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加'\ 0'太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是'\0'。不妨做个小实验验证一下。

最后对static的三条作用做一句话总结:首先static的最主要功能是隐藏,其次因为static变量存放在静态存储区,所以它具备持久性和默认值0。

4.static修饰函数

 在函数的返回类型前加上关键字static,函数就被定义成为静态函数。

 函数的定义和声明默认情况下是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用。好处:

1)其他文件中可以定义相同名字的函数,不会发生冲突

2)静态函数不能被其他文件所用。

二.extern关键字

1.extern关键字可以用来修饰变量,表示该变量在别的文件中已有声明

佷显然使用extern关键字修饰的变量都是全局变量,因为在其它文件中引用局部变量是不会有意义的,也超出了局部变量的作用域。

注:只能用于扩展没有被static关键字修饰的全局变量。默认情况下全局变量只能在定义它的文件中使用(从定义该全局变量开始到所在文件的文件尾),但如果在另一个文件中将这个变量声明为外部变量,那么这个变量的作用域将被扩展到另外一个文件中。也可以在定义全局变量之前声明该变量,从而在文件中可以在定义该全局变量前使用该全局变量。

​​​简单的理解就是:若一个变量需要在同一个工程中不同文件里直接使用或修改,则需要将变量做extern声明。只需将该变量在其中一个文件中定义,然后在另外一个文件中使用extern声明便可使用,但两个变量类型需一致。
方法一:(常用)

在1.c中定义全局变量
int i; 
 
在2.c和3.c中都用 
extern int i;
 
声明一下就可以使用了

方法二:

在头文件a.h 中声明 
extern int i; 注意这里只是声明,还需要在某个地方定义才行,并且定义的时候一定是全局变量
 
在其他某个c文件中定义,一定要是全局变量
int i =0; 
 
其他要使用i变量的c源文件只需要include"a.h"就可以

其实也可以在头文件t1.h里定义int a, t1.c里赋值,t2.c包含t1.h文件也可以使用a的值,但是可能不用这样的引用关系,用extern提高了效率而且更加规范。

2.extern修饰函数

从本质上来讲,变量和函数没有区别。函数名是指向函数二进制块开头处的指针。

如果文件a.c需要引用b.c中的函数,比如在b.c中原型是int fun(int mu),那么就可以在a.c中声明extern int fun(int mu),然后就能使用fun来做任何事情,而不用包含头文件

t3.h:
extern void play();

t3.c:
#include <stdio.h>
#include <string.h>
#include "t3.h"

void play()
{
        printf("this play \n");
}


t4.c:
#include<stdio.h>
#include<string.h>
extern void play(); //只用声明,就不用包含头文件了

void main()
{
play();
}

就像变量的声明一样,extern int fun(int mu)可以放在a.c中任何地方,而不一定非要放在a.c的文件作用域的范围中。

对其他模块中函数的引用,最常用的方法是包含这些函数声明的头文件。使用extern和包含头文件来引用函数有什么区别呢?extern的引用方式比包含头文件要简洁得多!extern的使用方法是直接了当的,想引用哪个函数就用extern声明哪个函数。

这样做的一个明显的好处是,会加速程序的编译(确切的说是预处理)的过程,节省时间。在大型C程序编译过程中,这种差异是非常明显的。一般来说,还是会以包含头文件的方式,也就是在t.h头文件里声明extern,在t.c里实现,t2.h包含t1.h头文件。也就包含了函数。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值