在C中使用extern变量

时间:2014-02-08 06:21:09

标签: c

当我通过extern变量概念时,我遇到了这个片段:

int array[10]; // in file1.c
extern int *array; // in file2.c

解释如下,数组的语义在两个文件中是不同的。链接器无法解析名称,同时不会标记错误,但程序在运行时失败。

但是当我尝试这个时,它既没有在运行时失败,也没有得到任何警告。

我怀疑是否可以使用任何语义,而使用extern意味着如果我的extern变量是int类型,则另一个变量可以是类型指针或数组!

这是正确的,有人可以解释一下吗?

5 个答案:

答案 0 :(得分:2)

你做不到。请参阅Jens Gustedt的答案并使用可接受的语法。

让我们考虑如果你定义一个数组并将它声明为另一个文件中的指针会发生什么。

int array[10];      // in file1.c
extern int *array;  // in file2.c

我们都知道,解释为不同符号的全局变量在编译中具有明确的地址。我们通过从他们的地址加载来获得变量值。所以在这种情况下如下:

int array[10];

如果我们想知道array[0]的价值,首先我们会找到符号array的地址,让我们说0x20132014。然后我们将索引0添加到此地址,并从此地址array[0]获取0x20132014的值;

在另一个案例中:

int *p;

同样,我们所拥有的只是符号p的地址。这是指针的地址。因此,如果我们想知道p[0]的价值,就应该采取额外措施。首先,我们得到符号p的地址,比如说0x12345678,然后找出p的值,它存储在0x12345678中。此值(例如0x10101010)是p指向的变量的地址(即p[0])。我们实际上是从地址0x10101010加载来获取p[0]的值。

让我们回过头来看看如果我们定义一个数组并将其声明为另一个文件中的指针会发生什么:

在file2中,我们希望使用array[0]来获取数组中第一个元素的值。但由于在此文件中,array被声明为指针,因此符号array的地址被视为指针的地址,因此存储在此地址中的值被视为{{1}的地址}}, 如上所述。但是,此值实际上是array[0]的值而不是地址。在这种情况下,我们尝试从不适当的地址加载,并可能导致运行时错误。

根是因为编译器在处理不同类型时定义了不同的方法。因此,请在所有声明中保持匹配的类型。

答案 1 :(得分:1)

不,你不能这样做,这种行为是未定义的。如果您不知道第二个文件中数组的大小,可以使用以下语法:

int array[10]; // in file1.c
extern int array[]; // in file2.c

这可以帮助您访问file2.c中的数组。你唯一不能做的就是sizeof array

答案 2 :(得分:1)

不,这不对。它的结构不正确,会导致未定义的行为或分段错误,具体取决于您对arrayfile2.c的处理方式。

file1.c中,您定义 arrayarray的10 int类型。在file2.c中,声明 arrayint *类型。在链接阶段,链接器将array中的标识符file2.cfile1.c中的定义链接起来。所以基本上,相同的内存位置在两个不同的文件中作为不同类型访问,这显然会导致问题。

如果您尝试在array中取消引用file2.c,那么根据系统上指针的大小,第一个元素或前两个元素将被解释为内存地址,并且最多可能导致分段错误。

// in file2.c
// may cause seg fault

int x = *array;

正确的方法是

// in file2.c
// sizeof may not be applied to array in this scope
// because array is incomplete type until linked.

extern int array[];

您必须将其声明中的变量类型与其外部定义相匹配。在解析符号引用时,链接器不检查变量的类型。只有函数具有编码到函数名称中的函数类型。因此,您不会收到任何警告或错误,它会编译得很好。

从技术上讲,链接器或某些编译器组件可以跟踪符号所代表的类型,然后给出错误或警告。但是标准没有要求这样做。你需要做正确的事。

答案 3 :(得分:0)

您需要知道array的类型为int [10],不能将其投放到int *

int array[10];        // in file1.c
extern int array[10]; // in file2.c

答案 4 :(得分:0)

你可以这种方式使用它.extern与另一种类型。 但它只会让你对阵列的确切类型感到困惑。 如果你在没有告诉返回类型的情况下extern一个函数,编译器将始终将返回类型设置为int。在某些情况下,你可能会得到切换值。

file1.c数组中的

是一个int 10数组。

file2.c数组中的

指针指向无,并且与file1中的数组具有相同的地址。

我使用gdb进行测试。

file2.c中的

数组是

(gdb) p array
$2 = (int *) 0x0
(gdb) p &array
$3 = (int **) 0x804a040

我为数组设置了数字。

(gdb) x/10 &array
0x804a040 <array>:  0   1   2   3
0x804a050 <array+16>:   4   5   6   7
0x804a060 <array+32>:   8   9

要访问file2.c中的数组,您需要将数组转换为int * type

(gdb) p/d ((int*)&array)[9]
$8 = 9
file1.c中的

是一个普通的数组。

(gdb) p array[2]
$11 = 2

请注意你不能像在file1.c中那样在file2.c中使用数组。你会得到一个分段错误。