0

头文件中语言数据类型声明的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;
4

2 回答 2

1

为什么这段代码要为声明这样做?因为作为DECLARE_REFERENCE显示的主体,当一个类型和名称被传递给这个宏时,它不仅仅做声明——它还从名称中构建了一些其他的东西,用于其他未知的目的。如果你只想声明一个变量,你不会这样做——它所做的事情与简单地声明一个变量不同。

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

为此使用宏可能有几个目的,我可以立即想到:

  1. 节省击键
  2. 使代码更具可读性——但仅限于已经知道宏含义的人
  3. 如果获取参考数据的次要方法仅用于调试目的,则可以在发布版本中轻松禁用它,从而在任何幸存的调试代码上生成编译器错误
  4. 它强制访问路径的次要状态,对那些只想查看结构及其正式接口中包含的内容的人隐藏它

你应该这样做吗?不。这不仅仅是声明变量,它还了其他事情,而其他事情显然特定于包含程序的其余部分的血腥内部。如果没有看到程序的其余部分,我们可能永远无法完全理解它的其余部分。

当您需要针对程序内部做一些特定的事情时,您将(希望)知道何时是时候发明自己的类似这样的东西(很可能永远不会);但不要抄袭别人。

所以这里的总体教训是找出人们不是用简单的 C 语言编写的地方,而是针对他们的特定应用程序进行编码的地方,并将这两者分开,而不是将特定程序的怪癖作为整个语言的指导方针。

于 2013-10-18T15:27:23.087 回答
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 代码。

于 2013-10-18T19:18:43.733 回答