这些 C 外部定义之间有什么区别?

What's the difference between these C external definitions?

我 运行 在调试 AVR 微控制器时遇到了这个问题: 我有一个 main.c 文件,其中包含许多变量定义,其中有一个结构数组,如下所示:

struct mystruct mystruct_array[COUNT];

在另一个 .c 文件中,我将这个数组称为外部数组,但我省略了数组括号和大小,所以我不会重复自己,只是将变量声明为指针(因为数组本质上是指针,不是不是吗?):

extern struct mystruct *mystruct_array;

但是当我使用 printf("%p\n", mystruct_array); 检查数组的地址时,我 得到了一个空指针 而不是数组在内存中的位置。此外,如果我要访问数组中的后续项,如 printf("%p\n", &(mystruct_array[n]));,它将打印地址 0 加 nsizeof(struct mystruct).

只有在我把定义改成

之后
extern struct mystruct mystruct_array[COUNT];

(和main.c中的完全一样),我得到了数组的真实地址

我的问题:为什么这会对编译器(在我的例子中是 avr-gcc)产生影响?

这很有趣。

当你写:

struct mystruct mystruct_array[COUNT];

您创建了一个 mystruct 结构的全局数组,其中有 COUNT 个,由于您没有对其进行初始化,因此它将被填充为零。

当你接着写:

extern struct mystruct *mystruct_array;

你告诉编译器某处有一个名为 mystruct_array 的变量,它是一个指针。但它不是,它是一个数组。所以编译器将读取数组的内容,就好像它是一个指针。

因此,当您尝试输出该指针的值时,编译器会在内存中获取 mystruct_array 并将其内容作为指针输出。由于它实际上是一个全为零的数组,因此您会在输出中看到一个空指针。

你应该这样写:

extern struct mystruct mystruct_array[];

所以编译器知道你变量的正确类型。您可以在此处的方括号中指定长度,但您不必指定。

我建议您继续阅读 the differences between pointers and arrays 以确保您以后不会混淆它们。

extern的声明意味着声明的对象在其他C文件中是全局的。然后,当您生成目标文件时,如果声明的对象(在本例中为结构)不存在,也会生成它。

如果您声明:

extern struct structname * s;

意味着在另一个 C 模块中有一个指向结构 structname s 的全局可见指针。

如果您声明:

extern struct structname s;

意味着在另一个C模块中有一个全局可见结构structname s!

当您将 link 程序时,如果您不向 linker 指示 link 包含该结构的对象,您将得到一个未定义的引用!

实现此目的的理想方法是将 extern 声明放在 header 文件中,并将定义放在同一个文件中。例如,

header.h

extern struct mystruct mystruct_array[];
/* or extern struct mystruct mystruct_array[COUNT] */

main.c

#include "header.h"

#define COUNT 10
/* should have an initializer to be a definition: */
struct mystruct mystruct_array[COUNT] = { 0 };

/* ... */

other.c

#include "header.h"

/* ... */
    printf("%p\n", mystruct_array);

这样可以避免重复,并限制您可能需要进行更改的地方。请注意,如果您的 header 没有定义数组中元素的数量,那么您不能将 sizeof 操作应用于除提供数组定义的文件之外的文件中的该数组。

还请注意,尽管 数组不是指针,但在 C 源代码的大多数上下文中,数组名转换为指向数组第一个元素的指针。这就是数组是指针的误解的来源。尽管它们不是指针,但在许多方面它们的行为就好像它们是指针一样。