代码之家  ›  专栏  ›  技术社区  ›  Norman Ramsey

为什么要在.c文件中避免使用#ifdef?

  •  30
  • Norman Ramsey  · 技术社区  · 15 年前

    我尊敬的一位程序员说,在C代码中, #if #ifdef 应该不惜一切代价避免,可能在头文件中除外。为什么使用它会被认为是糟糕的编程实践 #ifdef 在.c文件中?

    11 回复  |  直到 15 年前
        1
  •  57
  •   Alex Budovski    15 年前

    很难维持。更好地使用接口来抽象特定于平台的代码,而不是通过分散滥用条件编译 #ifdef 这一切都在你的实现中。

    void foo() {
    #ifdef WIN32
       // do Windows stuff
    #else
       // do Posix stuff
    #endif
       // do general stuff
    }
    

    这不好。而是有文件 foo_w32.c foo_psx.c 具有

    void foo() {
        // windows implementation
    }
    

    foo_psx.c:

    void foo() {
        // posix implementation
    }
    

    void foo();  // common interface
    

    然后有2个makefile 1. Makefile.win , Makefile.psx ,并分别编制适当的 .c

    轻微修订:

    foo() 的实现取决于所有平台中出现的一些代码,例如。 common_stuff() 2. ,只需在您的 foo()

    共同点h:

    void common_stuff();  // May be implemented in common.c, or maybe has multiple
                          // implementations in common_{A, B, ...} for platforms
                          // { A, B, ... }. Irrelevant.
    

    void foo()  {  // Win32/Posix implementation
       // Stuff
       ...
       if (bar) {
         common_stuff();
       }
    }
    

    而您可能正在重复对的函数调用 普通的东西 ,无法参数化的定义 每个平台,除非它遵循非常特定的模式。通常,平台差异需要完全不同的实现,并且不遵循这种模式。


    1. 这里使用makefile作为示例。您的生成系统可能不使用 make
    2. 即使
        2
  •  6
  •   paul    15 年前

    我曾经看到一条建议使用 #if(n)def/#endif 用于调试/隔离代码而不是注释的块。

    建议有助于避免将要评论的章节已经有文档评论的情况,并且必须实施如下解决方案:

    /* <-- begin debug cmnt   if (condition) /* comment */
    /* <-- restart debug cmnt {
                                  ....
                              }
    */ <-- end debug cmnt
    

    相反,这将是:

    #ifdef IS_DEBUGGED_SECTION_X
    
                    if (condition) /* comment */
                    {
                        ....
                    }
    #endif
    

    对我来说似乎是个好主意。希望我能记住来源,这样我就可以链接它:(

        3
  •  3
  •   Simeon Pilgrim    15 年前
    1. 因为它们应该用于操作系统/平台依赖关系,因此此类代码应该在io_win.c或io_macos.c之类的文件中

        4
  •  3
  •   catchmeifyoutry    15 年前

    我对这条规则的解释是: 您的(算法)程序逻辑不应受到预处理器定义的影响。代码的功能应该始终简洁。任何其他形式的逻辑(平台、调试)都应该可以在头文件中抽象。

    这与其说是一条严格的规则,不如说是一条指导方针。 但我同意基于c语法的解决方案比预处理器更受欢迎。

        5
  •  2
  •   Community CDub    5 年前

    条件编译很难调试。人们必须知道所有的设置,才能知道程序将执行哪一块代码。

    我曾经花了一周时间调试一个使用条件编译的多线程应用程序。问题是标识符的拼写不同。使用了一个模块 #if FEATURE_1 #if FEATURE1 (注意下划线)。

    makefile 通过包含正确的库或对象来处理配置。使代码更具可读性。此外,大多数代码与配置无关,只有少数文件与配置相关。

        6
  •  2
  •   DigitalRoss    15 年前

    一个合理的目标,但没有严格的规则那么伟大

    然而,有很多代码看起来像下面的虚构示例,我不认为有更好的选择。我认为你引用了一条合理的指引,但不是一条伟大的金字戒律。

    #if defined(SOME_IOCTL)
       case SOME_IOCTL:
       ...
    #endif
    #if defined(SOME_OTHER_IOCTL)
       case SOME_OTHER_IOCTL:
       ...
    #endif
    #if defined(YET_ANOTHER_IOCTL)
       case YET_ANOTHER_IOCTL:
       ...
    #endif
    
        7
  •  1
  •   Peter Cordes    15 年前

    CPP是一个独立的(非图灵完成)宏语言(通常)C或C++。因此,如果不小心,很容易混淆它和基础语言。这是通常的反对宏的论点,而不是C++模板。但是"如果定义"呢??试着去读别人的代码,你以前从未见过,那有一堆ifdef。

    http://parchive.cvs.sourceforge.net/viewvc/parchive/par2-cmdline/reedsolomon.cpp?revision=1.3&view=markup

    如果根据构建需要不同的底层行为,请尝试将其封装在底层函数中,以便在任何地方都提供一致的行为,而不是将#If内容放在更大的函数中。

        8
  •  1
  •   Steve Emmerson    15 年前

    无论如何,支持抽象而不是条件编译。然而,任何编写过便携软件的人都可以告诉你,环境变化的数量是惊人的。一些设计原则可能会有所帮助,但有时需要在优雅和满足时间表之间做出选择。在这种情况下,可能需要妥协。

        9
  •  1
  •   Wolf    4 年前

    考虑您需要提供完全测试代码、具有100%个分支覆盖率的情况,现在添加条件编译。

    用于控制条件编译的每个唯一符号将使需要测试的代码变体数量加倍。因此,一个符号-您有两个变体。两个符号,您现在有四种不同的方式来编译代码。等等

    这只适用于布尔测试,例如 #ifdef . 如果测试的形式是这样的,您可以很容易地想象问题 #if VARIABLE == SCALAR_VALUE_FROM_A_RANGE .

        10
  •  0
  •   Chip Uni    15 年前
        11
  •  0
  •   Kevin    15 年前