C语言里面的extern

extern是C语言中比较灵活的一个关键字,和指针一样,如果应用不当就比较麻烦,它一般用来声名一个外部变量,所谓的外部变量是指在函数或者文件外部定义的全局变量。外部变量定义必须在所有的函数之外,且只能定义一次。

C语言中定义的全局变量的作用域一般是从定义位置开始到文件结束,如果其它文件需要使用这些全局变量,就需要用到extern关键字。不过最近在用extern关键字的时候遇到一个问题,这是记录一下。

问题描述

问题是这样的,我们定义了一个全局的数组常量,其它文件需要用到这些结构体,在定义的时候我们是这样写的:

// filename: arrays.c
const int my_array[] = {1, 2, 3, 4, 5};
// filename: arrays.h
extern const int my_array[];

上面第一个是数组的实际实现,第二个是一个外部引用的声名,比如要在另一个文件中使用,我们可以通过如下的方式实现:

// filename: main.c
#include <stdio.h>

#include "arrays.h"

int main(){
    printf("my_array[0]=%d\n", my_array[0]);
}

这样程序运行是没有问题的,不过如果我们对数组进行sizeof运算就会出现问题:

#include <stdio.h>

#include "arrays.h"

int main(){
    printf("my_array[0]=%d\n", my_array[0]);
    printf("sizeof(my_array)=%d\n", sizeof(my_array));
}
// output when compile
main.c: In function ‘main’:
main.c:7:36: error: invalid application of ‘sizeof’ to incomplete type ‘int[]’
     printf("sizeof(my_array)=%d\n", sizeof(my_array));

对a进行sizeof运算的时候就提示类型定义不完整,我们来分析一下出现这个问题的原因,实际上因为extern只是告诉编译器有my_array这个全局变量,编译器不会关心这个全局变量的具体实现,也就是编译器不会去查找这个变量的具体定义,程序和变量my_array的正式关连是在链接阶段实现的。所以在编译阶段编译器并不会知道这个变量是个什么东西,实际即使my_array没有定义编译也不会报错(这种情况报错出现在链接阶段)。

所以上面这种情况,因为编译器没有办法知道数组的长度,所以报错,解决方法也有几种:

  1. extern数组的时候直接定义好长度,比如extern const int my_array[5];这样当数组长度有变化的时候需要同时修改两个地方。
  2. 额外定义一个变量用于表示数组的长度,比如:
// filename: arrays.c
const int my_array[] = {1, 2, 3, 4, 5};
const int my_array_len = sizeof(my_array)/sizeof(int);
// filename: arrays.h
extern const int my_array[];
extern const int my_array_len;

以上是两个基本的解决方法,实际还有一些比较复杂的方法,比如修改icf或者lds文件,直接定义好数组在代码的存放位置等等。这是就不展开了。

发表新评论