【问题标题】:Including C header file with lots of global variables包含具有大量全局变量的 C 头文件
【发布时间】:2010-05-19 19:18:19
【问题描述】:

我有一个包含 100 多个全局变量的包含文件。它正在库中使用,但我将库链接到的某些程序也需要访问全局变量。

它的建造方式:

// In one library .c file
#define Extern

// In the programs that use the globals
#define Extern extern

// In the .h file
Extern int a,b,c;

我很难理解为什么最初的程序员会这样做,所以我删除了定义外部的东西。现在我想我在stackoverflow的帮助下理解了关于TU的事情: 1, 2, 3.

现在我明白我应该在库中的一个 .c 文件中定义全局变量,并在 .h 文件中使用 extern。问题是我不想重复代码。

我应该回到那个#define Extern voodoo 吗?

【问题讨论】:

  • 很抱歉最近出现,但接受的答案是错误的方法。 Frederik Slijkerman 的答案是正确的。只有 .h 文件中的声明和 .c 文件中的定义。 extern var 是一个声明,没有extern 它是一个定义。将变量的定义放在模块(即 .c 文件)中,它是逻辑(语义)的一部分。相反,从不在 .c 文件中使用 extern var

标签: c gcc


【解决方案1】:

这里的诀窍是 .h 文件以两种不同的方式使用 - 它被用作普通的 .h 文件,其中所有全局变量都被声明为 extern 并且它也被用于定义 em> 全局变量本身(没有extern)。这是一个丑陋的 hack,但如果你有大量的全局变量,你可以理解为什么有人觉得有必要(这肯定是软件设计非常糟糕的迹象!)。

无论如何,有一个更优雅的解决方案 - 您可以将所有全局变量放在一个全局结构中,例如

//
// globals.h
//

typedef struct {
    int a;
    int b;
    // ...
    int z;
} Globals;

extern Globals globals; // declaration

-

//
// globals.c
//

#include "globals.h"

Globals globals; // definition

-

然后,当您需要引用全局时,例如globals.a 而不仅仅是 a,这可能看起来很不方便,但这可以说比在代码中散布裸露的全局变量更清晰、更易于管理。

【讨论】:

  • 你是对的,将这些全局变量放在某些结构中是一个 TODO。甚至可以给它们起一些有意义的名字。
【解决方案2】:

必须在每个 .c 文件中定义 Extern 是一种不好的模式。删除它可能是最好的,但您需要以某种方式替换此功能。一种方法是您可以在需要定义这些全局变量的 .c 文件中使用 #define。此定义将向 .h 发出信号,不要将全局变量外部化。

例如: 一个库.c文件:

#define FOO_LIBRARY_C
#include "foo_library.h"

其他 .c 文件:

#include "foo_library.h"

foo_library.h:

#ifdef FOO_LIBRARY_C
int a,b,c
#else
extern int a,b,c
#endif

#ifdef FOO_LIBRARY_C
#define GLOBAL_PREFIX
#else 
#define GLOBAL_PREFIX extern
#endif

GLOBAL_PREFIX int a,b,c

这减少了在每个源文件(一个除外)中定义相同内容的需要,并有助于减少错误。我也不会称它为 Extern,因为这只会引起混淆,因为它可能是也可能不是“extern”

【讨论】:

  • 我不同意这是一个糟糕的模式:我知道这违背了在 .h 文件中包含 externs 的感知智慧,但对于这种情况,除了不得不写一些额外的外部定义。如果您发现需要编写大量它们,那么您的代码就会变得一团糟(全局变量太多),无论如何您都应该始终考虑使用它们的方式和位置,因为它们可能会导致非常微妙的错误。通过使用 .h 文件,您可以无差别地使用全局变量。
  • Makis,你在这里混淆了两件事; - 使用全局变量的设计选择 - 必须使用全局变量时的最佳实践。很明显,使用全局变量通常不是一个好主意,但是当您必须使用全局变量时,请使用其实现的最佳用法,即标题中的extern var 声明和右侧.c 文件中的var 定义。任何其他使用模式都是错误的。
【解决方案3】:

也许我也遗漏了一些东西,但我总是对我创建的所有头文件使用包含保护:

foo.h:

#ifndef FOO_H
#define FOO_H

extern int foo;

#endif

foo.c:

#include "foo.h"

int foo = 0;

bar.c:

#include "foo.h"
#include <stdio.h>
int main(int argc, char** argv)
{
    printf("foo:%d\n",foo);
    return 0;
}

【讨论】:

  • 我认为这种简单的策略不适用于包含相同头文件的库和程序。
  • 比起 Aaron 的方法,我更喜欢这里也列出的方法。将定义放在一个文件中,将声明放在另一个文件中对我来说更有意义,而且似乎是一种更常见的模式。
  • 各位,这行不通。想象一下,如果我编译该库,并在以后编译一个包含该 .h 文件的程序。两者都将具有没有外部的全局变量。这就是我所描述的情况。
  • @Costi,foo.c 是库的一部分,因此全局变量在库中。您的程序都将包含 foo.h - 全局变量在外部定义。包含保护将允许每个单独构建的程序包含一次 foo.h。你能解释一下这是怎么坏的吗,因为我看不到它。
  • +1。您不应该使用包含头文件来包含“代码”(相对于定义)。我知道这条线在这里很模糊,但是关闭 extern 关键字以有效地使标题功能作为定义的想法有效,但这只是管理“太多”全局变量的副产品(我知道这是主观的,但我'正在解释为什么这种模式在野外并不常见。)
【解决方案4】:

宏的东西对此很愚蠢。随便放

extern int myGlobal;

在您的头文件和一个 .c 文件(通常与 .h 文件同名)中,放入

int myGlobal;

无需为这种“重复”级别感到压力。

【讨论】:

  • 这应该是公认的答案。这是唯一理智的方法。其他答案中提供的任何其他预处理器杂技都是错误的,在代码中添加了不必要的噪音。
【解决方案5】:

我可能遗漏了一些东西,但我认为 #define 和仅使用 extern 关键字之间没有区别。

基本上,您必须在 .h 文件中声明变量并在 .c 文件中定义它们。不要认为它是代码重复——我认为改变观点是你可以在这里做的最好的事情:)。您可以编写更多代码行,但它们将是可读的:D。

【讨论】:

  • 我愿意将 EXTERN 放置在声明和头文件中,以便更容易使用值 jsut 的剪切和粘贴,并有一个视觉问题,即 var 是全局范围可见。话虽如此,我对这无处不在的美妙滥用感到不寒而栗。哦,意大利面条 var 跟踪的日子......
  • 好吧,我是为 dll_export 等做的,但那是因为 M$VC 和 gcc 做 DLL 的方式不同。至于外部人员,我非常喜欢只为他们提供一个 .h 和一个 .c 文件。就像我说的,那是更多的代码,但我喜欢可读性。也许这只是个人喜好,我不知道。再说一次,如果我要编写不可读的代码,我会学习用 Brainf*ck 或类似的东西编程:)
  • 这正是我以某种方式指定全局变量名的原因,尽管我非常讨厌匈牙利符号。根据程序的风格,我使用“g”或“g_”前缀。它对检查的帮助超出了您的想象。
  • +1 我认为我会走得更远。永远不要在标题中定义全局变量,永远不要,没有例外,它只会带来痛苦和悲伤。包含文件应该是幂等的。
【解决方案6】:

在大型程序中,单行声明和定义全局变量非常重要。因此,上述宏观方法是解决问题的正确方法。

// globals.h 
#ifndef GLOBALS_H 
#define GLOBALS_H  
#ifndef EXTERN 
#define EXTERN extern 
#endif
EXTERN int i; 
#endif  

// globals.c 
#define EXTERN 
#include "globals.h" 

【讨论】:

    【解决方案7】:

    根据经验 - 不要使用全局变量。有时您需要在文件中使用静态变量,但最好尽量避免使用它们:

    • 您无法正确地对包含全局变量的代码进行单元测试
    • 模块之间没有明确的分离可能会导致错误隔离问题
    • 通常不是线程安全的。你必须用互斥锁等包装它们。如果你想使用线程(随着我们获得越来越多的内核,这往往是更好的主意)你可能会遇到可写共享状态的麻烦。

    有时在 C 中你无法避免它们(尤其是在由某人继承的代码中),但最好将它们排除在外。

    至于声明 - 在这种情况下可能会有所帮助:

    // globals.h
    
    #ifndef GLOBALS_H
    #define GLOBALS_H
    
    #ifndef EXTERN
    #define EXTERN extern
    #endif
    
    EXTERN int i;
    #endif
    
    // globals.c
    #define EXTERN
    #include "globals.h"
    

    【讨论】:

      【解决方案8】:

      虽然起初这可能很烦人,但它确实可以帮助您避免输入两次或完全忘记在 .c 文件中包含某些内容。 我看过:

      #define FOO_C_
      
      #include "foo.h"
      #include "bar.h"
      
      int foo_doit(int a, int b, int c) {
      ...
      }
      

      foo.h 是:

      #ifndef FOO_H_
      #define FOO_H_
      
      #ifdef FOO_C_
      #define GLOBAL
      #define DECLARE( type, name, value) type name = value
      #else
      #define GLOBAL extern
      #define DECLARE( type, name, value) extern type name;
      #endif
      
      GLOBAL int foo_doit(int a, int b, int c);
      GLOBAL int foo_it; // uninitialized global variable
      DECLARE(char, that[], "that");
      
      // and sometimes using:
      #ifdef FOO_C_
      char word[] = letters;
      #else
      extern char word[];
      #endif
      
      
      #endif // FOO_H_
      

      【讨论】:

      • 在函数声明中使用 GLOBAL 是不必要的。第二个#define DECLARE 后面的分号是错误的。
      猜你喜欢
      • 1970-01-01
      • 2011-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-01
      • 2014-02-20
      • 1970-01-01
      • 2015-08-22
      相关资源
      最近更新 更多