为什么某些struct变量需要预处理器才能运行?

时间:2013-10-18 02:37:26

标签: macros struct preprocessor c-preprocessor

由头文件中的语言数据类型声明的struct变量。通常数据类型用于声明变量,但其他数据类型传递给预处理器。当我们应该使用数据类型发送到预处理器来声明变量?为什么数据类型和变量会发送到处理器?

#define DECLARE_REFERENCE(type, name) \
union { type name; int64_t name##_; }

typedef struct _STRING
 {
   int32_t flags;
   int32_t length;

   DECLARE_REFERENCE(char*, identifier);
   DECLARE_REFERENCE(uint8_t*, string);
   DECLARE_REFERENCE(uint8_t*, mask);
   DECLARE_REFERENCE(MATCH*, matches_list_head);
   DECLARE_REFERENCE(MATCH*, matches_list_tail);

   REGEXP re;

 } STRING;

2 个答案:

答案 0 :(得分:1)

为什么代码正在为声明执行此操作?因为正如DECLARE_REFERENCE的主体所示,当一个类型和名称被传递给这个宏时,它不仅仅是声明 - 它还会在名称之外构建其他东西,用于其他一些未知目的。如果想要声明一个变量,那么你就不会这样做 - 它只是简单地声明一个变量。

它实际上做了什么?宏声明的联合提供了第二个名称,用于访问相同的空间作为不同的类型。在这种情况下,您可以获取引用本身,或者也可以获取其位模式的未转换整数表示。假设int64_t与目标上的指针大小相同,无论如何。

使用宏来实现这个目的可能有几个目的,我可以想到这一点:

  1. 保存按键
  2. 使代码更具可读性 - 但给已经知道宏意味着什么的人
  3. 如果获取参考数据的第二种方式仅用于调试目的,则可以轻松禁用发布版本,在任何幸存的调试代码上生成编译器错误
  4. 它强制执行访问路径的 secondary 状态,将其隐藏在只想查看结构及其正式接口中包含的内容的人
  5. 你应该这样做吗?不。这不仅仅是声明变量,做了其他事情,而其他事情显然特定于包含程序其余部分的血腥内部。如果没有看到程序的其余部分,我们可能永远不会完全理解它的其余部分。

    当你需要针对你的程序的内部做一些特定的事情时,你(希望)知道什么时候发明你自己的东西 - 就像这样(很可能永远不会);但不要复制别人。

    因此,这里的总体教训是确定人们不是直接编写C的地方,而是编写特定应用程序的编码,并将这两者分开,而不是将特定程序中的怪癖作为语言的指导。一个整体。

答案 1 :(得分:0)

有时需要保证一些声明可以保证彼此之间有某种关系。一些简单的关系,例如需要连续编号的常量,可以使用enum声明来处理,但是某些应用程序需要更复杂的关系,而编译器无法直接处理这些关系。例如,可能希望有一组枚举值和一组字符串文字,并确保它们保持彼此同步。如果有人宣称:

#define GENERATE_STATE_ENUM_LIST \
  ENUM_LIST_ITEM(STATE_DEFAULT, "Default") \
  ENUM_LIST_ITEM(STATE_INIT, "Initializing") \
  ENUM_LIST_ITEM(STATE_READY, "Ready") \
  ENUM_LIST_ITEM(STATE_SLEEPING, "Sleeping") \
  ENUM_LIST_ITEM(STATE_REQ_SYNC, "Starting synchronization") \
  // This line should be left blank except for this comment

然后代码可以使用GENERATE_STATE_ENUM_LIST宏来声明enum类型和字符串数组,并确保即使从列表中添加或删除项目,每个字符串也会与其正确匹配枚举值。相反,如果数组和枚举声明是分开的,则向一个而不是另一个添加新状态可能导致值“不同步”。

我不确定特定情况下宏的用途是什么,但模式有时可能是合理的。最大的“问题”是(ab)使用C预处理器是否更好,以便允许这种关系以有效但丑陋的C代码表示,或者使用其他工具获取列表是否更好状态并将从中生成适当的C代码。