限制#define标签的范围

时间:2012-09-28 13:36:34

标签: c++ c scope include c-preprocessor

限制#define标签范围并避免不必要的令牌冲突的正确策略是什么?

在以下配置中:

MAIN.C

# include "Utility_1.h"
# include "Utility_2.h"
# include "Utility_3.h"
VOID Main() { ... }

Utility_1.h

# define ZERO "Zero"
# define ONE  "One"
BOOL Utility_1(); // Uses- ZERO:"Zero" & ONE:"One"

Utility_2.h

# define ZERO '0'
# define ONE  '1'
BOOL Utility_2(); // Uses- ZERO:'0' & ONE:'1'

Utility_3.h

const UINT ZERO = 0;
const UINT ONE = 1;
BOOL Utility_3(); // Uses- ZERO:0 & ONE:1

注意: Utility _1Utility_2Utility_3已经独立编写


  

错误:宏重新定义和令牌冲突
  另外,最令人担忧的是:编译器没有说明什么替换了令牌替换的内容

{编辑} 注意:这是一般性问题,所以请:不建议enumconst

即。做什么时候:我必须使用#define& _请在下面评论我提出的解决方案.. __

9 个答案:

答案 0 :(得分:12)

正确的策略是不使用

#define ZERO '0'
#define ONE  '1'

完全没有。如果您需要常量值,请使用,在这种情况下,使用const char包装在命名空间中。

答案 1 :(得分:6)

#define没有与C ++代码对应的范围;你无法限制它。它们是天真的文本替换宏。想象一下,当我用grep替换文本时,我该如何限制范围?“

你应该尽可能地避免使用它们,而不是使用真正的C ++类型。

正确使用宏几乎可以通过命名约定来解决这个问题。如果宏被命名为对象,它应 一个对象(而不是宏)。问题解决了。如果宏被命名为函数(例如动词),它应该 一个函数。

这适用于文字值,变量,表达式,语句......这些都不应该是宏。这些都是可以咬你的地方。

在其他情况下,当你使用某种类型的语法助手时,你的宏名称几乎肯定不符合其他任何命名约定。所以问题几乎消失了。但最重要的是,当命名冲突时,需要成为宏的宏将导致编译错误。

答案 2 :(得分:6)

一些选项:

  1. 对宏与普通标识符使用不同的大小写约定。

    const UINT Zero = 0;

  2. 通过在宏中添加模块名称伪造命名空间:

     #define UTIL_ZERO '0'
     #define UTIL_ONE  '1'

  3. 在可用的地方(C ++),沟通宏并使用真正的命名空间:

     namespace util {
         const char ZERO = '0';
         const char ONE  = '1';
     };

答案 3 :(得分:5)

  

限制#define范围并避免无法解决的令牌冲突的正确策略是什么。

  1. 除非确实需要,否则请避免使用宏。在C ++中,通常可以使用常量变量和内联函数。它们具有键入的优点,并且可以在命名空间,类或代码块中进行范围限定。在C中,需要更频繁地使用宏,但在引入之前要仔细考虑替代方案。

  2. 使用命名约定来明确哪些符号是宏,哪些是语言级别的标识符。保留ALL_CAPITALS名称以供独占使用宏是很常见的;如果你这样做,那么宏只能与其他宏发生冲突。这也使人们更加关注更容易出错的代码部分。

  3. 在每个宏名称上包含一个“伪命名空间”前缀,以便来自不同库/模块/任何内容的宏和具有不同目的的宏不太可能发生冲突。因此,如果您正在设计一个想要为数字零定义字符常量的狡猾库,请将其命名为DODGY_DIGIT_ZERO。只有ZERO可能意味着许多事情,并且很可能与由不同的狡猾的图书馆定义的零价值常数冲突。

答案 4 :(得分:4)

有两种类型的#define宏:

  1. 仅在单个文件中需要的一个。我们称他们为Private #defines 例如。 PI 3.14在这种情况下:

    按照标准做法:正确的策略是将#define标签 - 仅放在实施中,即。 c ,文件而不是标题 h 文件。

  2. 多个文件需要的另一个:让我们称这些Shared #defines为 例如。 EXIT_CODE 0x0BAD在这种情况下:

    只在标题 #define 文件中放置此类常用h标签。

  3. 另外,尝试使用False NameSpaces或类似约定命名标签唯一,例如在标签前添加MACRO_,例如:#define MACRO_PI 3.14,以便发生碰撞概率减少

答案 5 :(得分:1)

  

限制#define范围并避免无法解决的令牌冲突的正确策略是什么。

一些简单的规则:

  1. 将预处理程序令牌的使用降至最低 有些组织甚至走这条路,只将预处理器符号限制为#include警卫。我没有这么做,但最好将预处理器符号保持在最低限度。
    • 使用枚举而不是命名整数常量。
    • 使用const static变量而不是命名浮点常量。
    • 使用内联函数而不是宏函数。
    • 使用typedef而不是#defined类型名称。
  2. 采用排除碰撞的命名惯例 例如,
    • 预处理程序符号的名称只能由大写字母和下划线组成。
    • 其他任何符号都不能包含仅由大写字母和下划线组成的名称。
  3.   

    const UINT ZERO = 0; // Programmer not aware of what's inside Utility.h

    首先,如果程序员没有找到Utility.h中的内容,为什么程序员会使用#include语句?显然UINT来自某个地方......

    其次,程序员通过命名变量ZERO来提出问题。保留预处理器符号的所有上限名称。如果您遵守规则,则无需知道Utility.h中的内容。简单地假设Utility.h遵循规则。将该变量的名称设为zero

答案 6 :(得分:0)

我认为你真的只需知道你包括的是什么。这就像试图包含windows.h然后声明一个名为WM_KEYDOWN的变量。如果你有碰撞,你应该重命名你的变量,或者(有点黑客),#undef它。

答案 7 :(得分:-2)

C是一种结构化编程语言。它有其局限性。这就是面向对象系统排在第一位的原因。在C中似乎没有其他方法,然后了解你的头文件的变量以_VARIABLE表示法开头的内容,以便它有更少的机会重写。

in header file 
_ZERO 0

in regular file

ZERO 0

答案 8 :(得分:-4)

  1. 我认为正确的策略是将#define标签 - 仅放在实施中,即。 c ,文件
  2. 此外,所有#define都可以单独放入另一个文件中 - 例如:Utility_2_Def.h
    (很像微软的WinError.h Win32 api函数的错误代码定义

    <强>间接费用:

    1. 额外档案
    2. 额外的#include声明
    3. <强>收益:

      1. 抽象:ZERO0'0'"Zero"关于您使用它的位置
      2. 更改整个模块的所有静态参数的一个标准位置

      <强> Utility_2.h

      BOOL Utility_2();
      

      <强> Utility_2_Def.h

      # define ZERO '0'
      # define ONE  '1'
      

      <强> Utility_2.c

      # include "Utility_2.h"
      # include "Utility_2_Def.h"
      
      BOOL Utility_2()
      {
          ...
      }