12

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

在以下配置中:

主程序

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

实用程序_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 _1 ,Utility_2Utility_3已独立编写


错误:宏重定义和令牌冲突
另外,最担心的是:编译器没有指出替换令牌的情况是什么

{Edit}注意:这是一个通用问题,所以请:不要提议enumconst

即在以下情况下该怎么做:我必须使用#define& _请在下面评论我提出的解决方案.. __

4

9 回答 9

11

正确的策略是不使用

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

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

于 2012-09-28T13:38:42.360 回答
11

有两种类型的#define宏:

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

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

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

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

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

于 2012-09-28T19:11:52.333 回答
8

#defines 没有对应于 C++ 代码的范围;你不能限制它。它们是天真的文本替换宏。想象一下问“当我用 grep 替换文本时如何限制范围?”

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

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

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

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

于 2012-09-28T13:40:24.170 回答
7

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

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

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

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

于 2012-09-28T13:51:31.350 回答
7

一些选项:

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

    常量 UINT 零 = 0;

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

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

  3. 在可用的情况下(C++),完全抛弃宏并使用真正的命名空间:

    命名空间实用程序{
         常量字符零 = '0';
         常量字符一 = '1';
     };

于 2012-09-28T13:54:38.220 回答
1

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

一些简单的规则:

  1. 尽量减少预处理器令牌的使用。
    一些组织甚至沿着这条路走,并将预处理器符号#include仅限于警卫。我没有走这么远,但是将预处理器符号保持在最低限度是个好主意。
    • 使用枚举而不是命名的整数常量。
    • 使用const static变量而不是命名的浮点常量。
    • 使用内联函数而不是宏函数。
    • 使用 typedefs 而不是 #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

于 2012-09-28T13:53:52.317 回答
0

我认为您真的只需要知道您所包含的内容。这就像试图包含 windows.h 然后声明一个名为 WM_KEYDOWN 的变量。如果你有冲突,你应该重命名你的变量,或者(有点像黑客),#undef it。

于 2012-09-28T13:41:12.873 回答
-2

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

in header file 
_ZERO 0

in regular file

ZERO 0
于 2012-09-28T17:18:32.863 回答
-3
  1. 我认为正确的策略是放置#define标签 - 仅在实施中,即。c, 文件
  2. 此外,所有#define内容都可以单独放在另一个文件中 - 比如说:(Utility_2_Def.h
    很像微软的WinError.hWin32 api函数的错误代码定义

    开销:

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

    收获:

    1. Abstraction: ZEROis: 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()
{
    ...
}
于 2012-09-28T13:36:33.893 回答