限制#define标签的范围

限制#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&_Please评论我提出的解决方案.. __

正确的策略是不使用

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

一点都不 如果你需要常量值,在这种情况下,使用const char代替包装在命名空间中。

一些选择:

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

     const UINT零= 0; 

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

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

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

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

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

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

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

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

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

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

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

  2. 使用命名约定,明确哪些符号是宏,哪些是语言级标识符。 保留ALL_CAPITALS名称以供独占使用宏是很常见的; 如果你这样做,那么宏只能与其他宏发生冲突。 这也吸引了更多可能存在漏洞的代码部分。

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

有两种类型的#define宏:

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

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

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

    只在标题h文件中放置这些常见的#define标签。

另外,尝试使用False NameSpaces或类似约定来唯一地命名标签,例如使用MACRO_前缀标签,例如: #define MACRO_PI 3.14以便减少碰撞概率

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

一些简单的规则:

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

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

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

其次,程序员通过命名变量ZERO来寻求麻烦。 保留预处理器符号的所有上限名称。 如果你遵守规则,你不必知道Utility.h里面有什么。 简单地假设Utility.h遵循规则。 将该变量的名称设为zero

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

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

 in header file _ZERO 0 in regular file ZERO 0 
  1. 我认为正确的策略是将#define标签放在实现中,即。 c ,文件
  2. 此外,所有#define都可以单独放在另一个文件中 – 例如: Utility_2_Def.h
    (很像微软的WinError.hWin32 api函数的错误代码定义

    管理费用:

    1. 一个额外的文件
    2. 额外的#include语句

    收益:

    1. 抽象: ZERO是: 0'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() { ... }