【问题标题】:Macros expansion using ## operator使用 ## 运算符的宏扩展
【发布时间】:2017-11-09 16:28:14
【问题描述】:

大家好,问题是我在这个宏中遇到了问题

 #define ADD_COMP(s1,s2,type)({\
  int _x=0;\
  for(int i=0;i<n_addrs;i++){\
    if(memcmp(s1,&(s2->##type),6)!=0){\
      _x=-1;\
    }else{\
      break;\
    }\
  }\
  _x;\
})

s1 是一个简单的数组,s2 是一个结构体,有 4 个向量作为成员,像这样

typedef struct example{
 char[6] one,
 char[6] two,
 char[6] three
}example;

现在出于自己的原因,我需要创建一个函数,将大小为 6 字节的 s1 数组与仅示例的成员进行比较,因此为此我使用 ## 运算符编写了 ADD_CMP,以使其尽可能通用 所以我定义:

#define one
#define two
#define three

我以这种方式多次使用函数,希望宏扩展成功

ADD_COMP(some_array,example1,one)
ADD_COMP(some_array,example1,two)
ADD_COMP(some_array,example1,three)

但编译器返回错误:

error: pasting "->" and "one" does not give a valid preprocessing token
error: pasting "->" and "two" does not give a valid preprocessing token
error: pasting "->" and "three" does not give a valid preprocessing token

如何在不为每个结构成员编写相同函数的情况下修复它?

【问题讨论】:

  • 编写一个函数并将该宏放入垃圾中。此外,您不需要按照错误提示将type 粘贴在-&gt; 后面,只需写s2-&gt; type
  • 确定一、二和三为空并不能说服我。如果删除这些定义会发生什么?另外,请发送minimal reproducible example。定义顺序等在这里是相关的。
  • char[6] one, 应该是char one[6];

标签: c macros expansion


【解决方案1】:

正如错误所暗示的,从来不需要##,因为它用于粘贴两个预处理标记以形成一个。

#define VAR(name, num) name##num
int VAR(foo, 1);  // foo and 1 must be pasted together as foo1, instead of foo 1

该宏应该在编译时修复几个语法错误和缺少的声明

 #include<stdlib.h>
 #include<string.h>

 int n_addrs = 6;

 #define ADD_COMP(s1,s2,type) {\
  int _x=0;\
  for(int i=0;i<n_addrs;i++){\
    if(memcmp(s1,&(s2->type),6)!=0){\
      _x=-1;\
    }else{\
      break;\
    }\
  }\
  _x;\
}

typedef struct example{
 char one[6];
 char two[6];
 char three[6];
}example;

void foo(void)
{
    example* example1 = malloc(sizeof(example));
    char some_array[6];
    ADD_COMP(some_array,example1,one)
    ADD_COMP(some_array,example1,two)
    ADD_COMP(some_array,example1,three)
}

注意compound statement { ... } 不是表达式,不能这样使用。通过在其周围添加额外的括号,您使用的是 gnu's extension 并且不是标准 C。

您应该改为编写一个函数来执行此操作。然后,您将能够返回 _x,并且使用现代优化器,开销应该可以忽略不计。

【讨论】:

  • 仅供参考,这并不是一个复合语句本身;这是一个compound statement in parentheses;一个 gnu 扩展。
  • @HWalters 有趣的是,我不知道扩展程序并“修复”它,假设它是一个错误
【解决方案2】:

标记粘贴操作符专为您希望将两个不同的预处理器标记粘合到一个标记中的情况而设计。例如,您可能会写类似

#define Glue(x) x my##x_

然后写

Glue(int);

获取此变量声明:

int my_int;

在这里,令牌粘贴将令牌“my_”与令牌“int”组合在一起形成新令牌“my_int”,这是一个表示名称的新单个令牌。

将两个标记粘贴在一起后,预处理器不会重新扫描它们以确定它是否是多个不同单个标记的组合。它将任何形成的内容视为单个令牌。例如,这段代码不会编译:

#define BadAdd(x, y) x##+##y
int z = BadAdd(137, 42);

这里的问题是令牌粘贴形成单个预处理令牌137+42。然后预处理器尝试将此预处理标记映射到单个逻辑标记,但没有可以对应的单个标记。通常,C 或 C++ 会将其视为三个单独的标记(137、+ 和 42),但由于您将它们强行粘合在一起,编译器不知道它在看什么。

将此与更传统的 Add 宏进行对比,为了说明,它省略了大量重要的括号:

#define Add(x, y) x + y
int z = Add(137, 42);

在这里,Add(137, 42) 扩展为三个标记 (137, +, 42) 的序列,编译器在稍后阶段可以将其解释为加法表达式。

您在上面编写的宏类似于 BadAdd 宏。通过将 -> 标记与字段名称粘合在一起,您最终会得到像 -&gt;one 这样的单个单元,编译器无法将其有意义地解释为单个标记。只需在此处删除 ## - 就像从 BadAdd 到 Add 一样,这将生成一系列标记而不是单个标记,这正是您想要的。

【讨论】:

    【解决方案3】:

    我不确定您为什么尝试使用 ##pre-processor 令牌。

    宏可以是:

    #define ADD_COMP(s1, s2, member) ({\
      int _x = 0;\
      for (int i = 0; i < n_addrs; i++){\
        if(memcmp(s1, &(s2.member), 6) !=0 ){\
          _x = -1;\
        } else {\
          break;\
        }\
      }\
      _x;\
    })
    

    并用作:

    ADD_COMP(some_array,example1,one);
    ADD_COMP(some_array,example1,two);
    ADD_COMP(some_array,example1,three);
    

    更重要的是,宏看起来很简单,可以用函数替换它,这总是一个更好的选择。我无法建议如何将宏转换为函数,因为我不清楚 n_addrs 来自哪里以及在哪里以及如何使用 _x

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-12
      • 2022-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多