在C语言中有两个特殊的关键字externstatic。两者既可以修饰函数也可以修饰变量,但意义上就完全不一样了。

static关键字

static关键字用于修饰变量或函数的作用域与性质。

修饰变量的情况下也可以分为两种情况:

而修饰函数只有一种可能,与修饰局部变量一样,对外部文件不可见。

extern关键字

static关键字关键字对立,extern用于声明一个函数。

修饰一个变量时则表示该变量被声明了,但仍未定义!

其中分为两种情况:

extern修饰函数:

修饰函数时可以提前声明一个函数,在链接时再做出决定,这一点与上面修饰变量非常相似。

其中值得一提的是如果函数被定义在b.c文件中,且b.h公开了一个名为void print()的方法,那么在a.c中使用#include <b.h>则可以在链接时使用void print()方法。除此以外如果不导入b.h头文件则可以使用extern void print()的方法声明,在最终链接时效果时一样的。

如果同时使用static extern修饰会怎样?

// filename: a.c

extern int b;
int main(void) {
    printf("%d\n", b);
}
// filename: b.c

static int b = 39;

下面是我在我的平台下做的测试:

urain39@urain39-PC:/tmp/tmpc$ clang -o a *.c
/tmp/a-6c5a84.o:在函数‘main’中:
a.c:(.text+0x15):对‘b’未定义的引用
clang: error: linker command failed with exit code 1 (use -v to see invocation)

可见extern即使可以修饰外部引用,但是在static修饰的变量下也还是不能被引用的。

关于extern中难以理解的bug

上面我们说过了extern可以引用外部变量,但有时编译器也会分不清extern的变量类型。

// filename: b.c

char str1[] = Hello;

// filename: a.c


extern char *str1;

int main(void) {
    printf("%s\n", str1);
}

以上代码在编译时并不会有报错,但是最终运行时会发生段错误,但在将extern char *str1改为extern char str[]以后则可以正常运行。实际上这是因为在传参过程中很多人习惯于将char *str1char str1[]混用,而实际上C语言里除了在函数参数里可以这么写以外,在其他时候str1依旧是被当做是str1数组的初始地址。在a.c中extern char *str1可能被理解为char **str1了。

这里引用billow_zhang大神的答案:

这个问题是因为你的extern char a[]的类型是数组类型,而实际的a的类型是char的指针类型。在C语言里面规定, 数组的名称代表的是数组的起始地址。

小结:虽然平时感觉不到指针与数组的差别,但是在编译器的理解上数组和指针还是相差十万八千里的。所以如果不是在传参时使用数组,还是请务必写成数组的形式。