今天在整理入职考试时候的错题,发现还有很多不会的地方,所以打算写一些博客记录下来。
externextern是C语言里面的关键字,用于修饰变量和函数,被extern修饰说明变量和函数不在本文件内或在使用的时候还没有被定义。
这里我们举一个例子来解释,这个例子里面有两个文件extern.c和extern_2.c
extern.c的内容如下:
#includeextern int func(); int main() { func(); extern int num; printf("%d", num); return 0; }
extern_2.c的内容如下:
#includeint func() { extern int num; printf("%dn", num); } int num = 7;
extern.c中用到了func函数和num变量,但是具体的内容是在extern_2.c中定义的,在编译的时候将两个文件一起编译,就可以正常执行main函数。
extern_2.c中,在输出num的时候num还没有定义,因此添加了extern int num,num变量之后再定义。
这就是extern的几种典型的用法,引用其他文件的变量/函数和引用之后才出现的变量/函数。
extern真正的意思是通知编译器,此处只是该变量或函数的声明,不会分配内存,而真正的定义在别处。
同时,extern定义的变量或函数在另一个文件里必须是全局的,本文件局部变量不能被其他文件引用。
extern声明的变量在函数外时,才可以进行初始化。
同时一个变量只能初始化一次。
这里我们举一个例子
#includeint main() { extern int num = 6; printf("%dn", num); } int num = 7;
这样会直接出现编译错误
extern_2.c: In function ‘main’:
extern_2.c:7:16: error: ‘num’ has both ‘extern’ and initializer
7 | extern int num = 6;
| ^~~
解析:num变量是在最后一行被定义的,之后extern不能再对其进行初始化。
备注:默认所有的函数和全局变量都可以被extern修饰。
在这一部分我由于失误发现了两个小问题
Question1:
#includeint num = 7; int main() { int num; printf("%dn", num); }
输出
0
全局变量与局部变量同名的情况下,优先使用了局部变量。
同时局部变量没有初始化的情况下,gcc会将其初始化,并初始化为0。我本来以为会直接报错,其实编译器还是蛮智能的。据我查找的资料,Windows上也是这么处理的。
具体初始化为多少这件事在Windows与Linux上是不同的,Windows会将其初始化为-858993460。
Question2:
extern.c
#includeextern int func(); int main() { func(); extern int num; printf("%d", num); return 0; }
extern_2.c
#includeint num = 7; int func() { int num; printf("%dn", num); }
输出
gcc -o extern extern.c extern_2.c ./extern 22087 #每次输出的值都不一样 7
这是另外一种情况了,同样是全局变量和局部变量同名,局部变量没有初始化,但是这次编译器没有单独对局部变量进行初始化,是直接输出了内存里存储的内容,原因暂时不明,这里先记录一下。等我日后研究明白了补上。
static
被static修饰的全局变量和全局函数只能在本文件内使用,不能被其他文件使用。它们被称为静态变量和静态函数。
还是以上面那段代码为例
extern.c的内容如下:
#includeextern int func(); int main() { func(); extern int num; printf("%d", num); return 0; }
extern_2.c的内容如下:
#includestatic int num = 7; static int func() { printf("%dn", num); }
输出
/usr/bin/ld: /tmp/ccc2TEVS.o: in function `main': extern.c:(.text+0xe): undefined reference to `func' /usr/bin/ld: extern.c:(.text+0x14): undefined reference to `num' collect2: error: ld returned 1 exit status
我只是在第一份代码的基础上在num和func前面用static修饰了,因此它们就不能被其他文件访问,会有报错找不到num和func的报错。
为了更进一步解释extern与static区别还需要引入存储方式:
- 静态存储:固定存储空间的变量,比如全局变量和static变量
- 动态存储:动态分配存储空间的变量,使用完成之后存储空间会回收,各种局部变量
存储相关的关键字
auto:在函数内使用,函数调用完结束则释放内存(局部变量默认为auto)
static:函数调用结束后不释放内存,保留值。
register:局部变量使用频繁,可以用register,意思是把变量放在CPU中的寄存器中,运算时可以更快的取到这个值,而不用去内存中取值
extern:可以跨文件使用。
2022.05.11日补充 static修饰局部变量的情况
这里再补充一下static的实际用法,明明刚刚研究过static的用法,还是有没学到的部分,学艺不精啊。
#includevoid fun(int *s) { printf("Start n"); static int j = 0; do { printf("Loop j = %dn", j); s[j] += s[j + 1]; } while (++j < 2); } int main() { int i, a[10] = {1, 2, 3, 4, 5}; for (i = 1; i < 3; i++) fun(a); for (i = 1; i < 5; i++) printf("a[%d]为%d t", i, a[i]); printf("n"); return 0; }
输出
Start Loop j = 0 Loop j = 1 Start Loop j = 2 a[1]为5 a[2]为7 a[3]为4 a[4]为5
这道题主要考察了static,do while还有变量的自增。
难点解析:
这里面用到static用于修饰局部变量int j,被修饰之后变量被保存在全局数据区,上一次的值会保持到下一次调用。
原因是j被static修饰之后会变成静态变量,静态变量在其先前的作用域中保留先前的值,不会在新的作用域中被再次初始化。因此第二次循环中j不是从0开始的而是从上一次的值开始。
同时j++是先自增再赋值,因此while只能循环两次。



