Macros 为什么struct的某些变量需要预处理器来运行?

Macros 为什么struct的某些变量需要预处理器来运行?,macros,struct,preprocessor,c-preprocessor,Macros,Struct,Preprocessor,C Preprocessor,头文件中语言的数据类型声明的结构变量。通常使用数据类型来声明变量,但其他数据类型传递给预处理器。什么时候我们应该使用数据类型发送给预处理器来声明变量?为什么数据类型和变量发送到处理器 #define DECLARE_REFERENCE(type, name) \ union { type name; int64_t name##_; } typedef struct _STRING { int32_t flags; int32_t length; DECLARE_REFE

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

#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;
为什么这段代码对声明执行此操作?因为正如
DECLARE\u REFERENCE
的主体所示,当类型和名称被传递给这个宏时,它所做的不仅仅是声明——它还从名称中构建了其他东西,用于其他未知的目的。如果你只想声明一个变量,你就不会这么做——它做的事情不同于简单地声明一个变量

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

为此使用宏可能有以下几种用途,我可以立即想到:

  • 保存击键
  • 使代码更具可读性,但仅限于那些已经知道宏含义的人
  • 如果获取引用数据的第二种方法仅用于调试目的,则可以在发布版本中轻松禁用它,从而在任何幸存的调试代码上生成编译器错误
  • 它强制执行访问路径的次要状态,对那些只想查看结构及其正式接口中包含的内容的人隐藏它
  • 你应该这样做吗?不,这不仅仅是声明变量,它还做了一些其他的事情,而另一件事情显然是特定于包含程序其余部分的血腥内部的。如果看不到程序的其余部分,我们可能永远无法完全理解它的其余部分

    当你需要做一些特定于你程序内部的事情时,你(希望)会知道什么时候是时候发明你自己的东西了(很可能永远不会);但不要抄袭别人


    所以这里的全部课程是确定人们不是用简单的C语言编写代码,而是为他们的特定应用程序编写代码的地方,并将这两者分开,不要把某个特定程序中的怪癖作为整个语言的指导方针。

    有时需要有一些声明,保证它们之间有某种关系。一些简单类型的关系,例如需要连续编号的常量,可以使用
    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代码