限制#define标签的范围

Ujj*_*ngh 5 c c++ scope include c-preprocessor

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

在以下配置中:

MAIN.C

# include "Utility_1.h"
# include "Utility_2.h"
# include "Utility_3.h"
VOID Main() { ... }
Run Code Online (Sandbox Code Playgroud)

Utility_1.h

# define ZERO "Zero"
# define ONE  "One"
BOOL Utility_1(); // Uses- ZERO:"Zero" & ONE:"One"
Run Code Online (Sandbox Code Playgroud)

Utility_2.h

# define ZERO '0'
# define ONE  '1'
BOOL Utility_2(); // Uses- ZERO:'0' & ONE:'1'
Run Code Online (Sandbox Code Playgroud)

Utility_3.h

const UINT ZERO = 0;
const UINT ONE = 1;
BOOL Utility_3(); // Uses- ZERO:0 & ONE:1
Run Code Online (Sandbox Code Playgroud)

注: Utility _1,Utility_2Utility_3已经独立编写


错误:宏重新定义和令牌冲突
另外,最令人担心的是:编译器没有指出替换更换令牌的内容

{编辑} 注意:这是一个通用的问题所以请:不建议enumconst

即做什么时候:我必须使用#define&_Please评论我提出的解决方案.. __

Luc*_*ore 12

正确的策略是不使用

#define ZERO '0'
#define ONE  '1'
Run Code Online (Sandbox Code Playgroud)

一点都不 如果您需要常量值,请使用,在这种情况下const char,包含在命名空间中.

  • 请注意,此答案仅适用于C++.在C中,`const char`不能用作编译时常量,因此可能不适合替换字符文字. (3认同)

Ujj*_*ngh 8

有两种类型的#define宏:

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

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

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

    #define在头h文件中放置这些通用标签。

此外,尝试使用或类似的约定来唯一地命名标签False NameSpacesMACRO_例如在标签前加上eg:#define MACRO_PI 3.14以便减少碰撞的概率


ten*_*our 6

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

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

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

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

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

  • 它们有一个范围,从定义的角度到相应的`#undef`或翻译单元的结尾. (5认同)
  • 当然它们确实有一个范围,但是它的范围适用于预处理器,而不是编译器. (2认同)

Mik*_*our 6

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

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

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

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


Joh*_*ode 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';
     };