【问题标题】:Proper External Variable Declarations and Definitions正确的外部变量声明和定义
【发布时间】:2023-09-12 20:55:01
【问题描述】:

在 C 中定义公开变量的公认标准方法是什么?假设设置如下:

在.h中

typedef struct my_struct{
...
} my_struct;

extern my_struct var1;

在.c中

my_struct var1;

这是正确的用法还是编译器在这里做了不必要的工作?外部人员实际上在这里做什么?我的理解一直是,默认情况下,一切都已经有一个隐含的外部。

【问题讨论】:

    标签: c extern


    【解决方案1】:

    在变量和函数方面,隐含的extern 部分存在细微差别。

    如果你放

    void foo(void);
    

    在一个 .h 文件中并将 .h 文件包含在多个 .cc 文件中,因为函数没有在 .h 文件中定义,所以没有任何危害。它只是声明的。

    如果你说,

    int x;
    

    在同一个 .h 文件中,然后在包含 .h 文件的每个 .c 文件中定义 x。如果 .h 文件具有以下功能,您将收到类似的函数错误:

    void foo(void){}
    

    因为这是一个声明,也是一个定义。

    要使x 等变量仅作为声明,您需要添加extern 关键字。

    【讨论】:

    • 我不知道如何将其中一个归功于答案,因为它们都在帮助我准确理解编译器发生了什么。这很有帮助。
    • @SeaNick 您可以编译所有答案并创建一个答案,其中包含您从所有答案中学到的想法。
    【解决方案2】:

    我的理解一直是,默认情况下,一切都已经有一个隐含的外部。

    这是真的,但是......

    在标头中明确externing var1的目的是:

    1. 记录你的意图,var1 不是私人的。
    2. 通知编译器源文件include 标头正在使用在别处声明的变量。

    推荐

    创建 getter/setter 函数并将 var1 设为私有 (static)。

    【讨论】:

    • 该建议提出了另一点:getter/setter 方法是真正确保多个 .c 文件为该变量创建定义并错误使用它们的唯一方法,对吧?
    • @SeaNick 不明白你的问题。特别是在多线程环境中,应该隐藏共享资源。 Getter/setter 函数将允许您将共享资源包装在互斥体中并避免竞争条件。
    • 我认为您的评论刚刚回答了我困惑的问题...如果您在两个单独的 .c 文件中定义变量,则每个文件都可能对该寄存器的当前状态做出错误的假设。
    • @SeaNick 关于您的示例,您只定义了一个变量。 include (extern) 的其他文件知道它的地址并且可以直接访问它(读/写)。不管include它有多少个源文件,只有一个变量,而不是每个包含一个。
    【解决方案3】:

    完全正确。您需要在标头中包含extern,以便告诉编译器var1 存在以及在编译使用它的代码时它的类型是什么。

    (我假设您在多个.c 文件中使用var1,其中一个正在定义它。如果您只在一个.c 文件中使用它,则无需如果您在使用之前定义了变量,则在标头中的声明。)

    【讨论】:

    • 那么下一个问题是,如果它的实例化是外部结构,它是否需要公开?不能将 typedef 移动到 .c 中并设为静态,或者如果另一个 C 文件引用了 extern 变量,这会导致编译器不知道如何访问其中的字段?
    • @SeaNick:后者。编译器需要查看 typedef 才能知道结构成员的名称和类型。
    • 声明必须在那个特定的头文件中,还是在使用该变量的任何其他 .c 文件中简单地创建一个 extern 声明是更好的做法?
    • @SeaNick:你应该把它放在标题中,然后它只在一个地方。重复通常被认为是一件坏事(“不要重复自己”原则)。
    • @SeaNick:不,没有预留空间。标题所做的只是说其他地方存在某些东西,这里有一些关于它的信息。 extern 表示“这是外部的,此处未定义,此处不占用任何空间。” (@RSahu 的回答很好地解释了这一点。)
    最近更新 更多