赋值宏编写

Assignment macro writing

我正在尝试为 for 循环中的以下重复代码编写一个宏。

for(i=0; i<n; i++) {
 a->x = b->x;
 a->y = b->y;
 a->z = b->z;
}
for(j=0;j<n;j++){
 a->x = c->x;
 a->y = c->y;
 a->z = c->z;
}

---------
with macro
#define COPY(x,y,z) \
a->x = x;\
a->y = y;\
a->z = z;\

for(i=0;i<n;i++)
 COPY(b->x,b->y,b->z);
for(i=0;i<n;i++)
 COPY(c->x,c->y,c->z);

我之前一直收到错误的意外表达式;


感谢您的快速回复, 看起来我更倾向于函数调用而不是宏,因为加班可能会很麻烦。 与使用函数调用而不是宏相比,是否有任何性能影响?

宏的情况很糟糕。你可以试试

#define COPYxyz(Dst,Src) do {   \
    (Dst)->x = (Src)->x;        \
    (Dst)->y = (Src)->y;        \
    (Dst)->z = (Src)->z;        \
   } while(0)

(注意 do{...}while(0) 在宏中是一个非常古老的 useful trick

然后代码 COPYxyz(a,b)(但是 COPYxyz(p++,--q)COPYxyz(++p,p) 是一场灾难,这就是为什么编写这样的宏是糟糕的原因)

但是,如果您有

  struct my_st  {int x, int y, int z};
  struct my_st *a = something();
  struct my_st *b = otherthing();

您可以编写一个结构赋值代码:

     *a = *b;

或者,假设您确定 ab 没有别名(不是同一个地址)并且没有重叠,您可以

     memcpy(a, b, sizeof(struct my_st));

并且如果结构包含超过 xyz 字段但您只想复制它们,请制作一个适当的 inline function:

static inline void copy_xyz(struct my_st*dst, const struct my_st*src) {
   assert (src != NULL);
   assert (dst != NULL);
   if (dst == src) return;
   dst->x = src->x;
   dst->y = src->y;
   dst->z = src->z;
   /// remaining fields are not copied!
}

然后您将毫无畏惧地编写 copy_xyz(p++,p) 代码,它应该与 COPYxyz 宏一样高效。如果您确定 assert-s 和 if (dst == src) return; 没用,您可以删除它们。

#define COPY(x,y,z) \
a->x = x;\
a->y = y;\
a->z = z;

通过时带上这个

COPY(b->x,b->y,b->z);

那么你有

a->b->x = b->x;
a->b->y = b->y;
a->c->y = c->y;;

这不是您需要的。 所以你需要将其替换为

#define COPY(p,q,r) do{\
a->x = p;\
a->y = q;\
a->z = r;\
}while(0)

I am trying to write a macro for the following repeated code inside the for loop.

请不要。它只有3行。您要做的就是使用晦涩且不安全的自制语言语法使代码混乱。您这样做已经成功地编写了一个致命错误:不对宏使用 {}。在大多数情况下,类似函数的宏是非常糟糕的做法。

首先,有没有什么原因不能写*a = *b;

如果有这样的原因,那么考虑做这样的事情:

typedef struct // given this struct
{
  int x;
  int y;
  int z;
} xyz_t;

void xyz_copy (xyz_t* dest, const xyz_t* source)
{
  dest->x = source->x;
  dest->y = source->y;
  dest->z = source->z;
}

#define COPY(x,y,z) \
a->x = x;\
...
对于 a->x = x; 中的两次出现,

都将替换 x,例如 COPY(b->x, ...) 给出 a->b->x = b->x,所以使用其他名称作为宏参数。


注意

for(i=0;i<n;i++)
 COPY(b->x,b->y,b->z);

将扩展到

for(i=0; i<n; i++)
  a->x = b->x;
a->y = b->y;
a->z = b->z;

这不是你想要的。养成总是将宏体放在 do { ... } while (0).

内的习惯

这个定义:

#define COPY(x,y,z) \
a->x = x;\
a->y = y;\
a->z = z;\

使用for循环

for(i=0;i<n;i++)
    COPY(b->x,b->y,b->z);
for(i=0;i<n;i++)
    COPY(c->x,c->y,c->z);

此快速测试的结果:

#include <stdio.h>

#define COPY(x,y,z) \
    a->x = x;\
    a->y = y;\
    a->z = z;\

int main( int argc, char* argv[] )
{
    for(i=0;i<n;i++)
        COPY(b->x,b->y,b->z);

    for(i=0;i<n;i++)
        COPY(c->x,c->y,c->z);
}

仅预处理:gcc -E main.c > main.i

给予

int main( int argc, char* argv[] )
{
    int i;

    for(i=0;i<n;i++)
        a->b->x = b->x; a->b->y = b->y; a->b->z = b->z;;

    for(i=0;i<n;i++)
        a->c->x = c->x; a->c->y = c->y; a->c->z = c->z;;
}

大错特错!

a->x = x中有两个地方发生了宏替换,因为两个x都被替换了,所以首先更改为唯一标识符。其次,不要在定义中对 copy-to 元素进行编码。在您的情况下,这是一个,将其传递给宏:

#define COPY( dest, x_copy, y_copy, z_copy ) \
    dest->x = x_copy; \
    dest->y = y_copy; \
    dest->z = z_copy;

您也落入了 if 后跟一个多语句宏的陷阱,它显然不会按照您的预期执行,因为您没有用花括号将多行宏括起来!在 do{ } while(0) 中包装多行宏是一种常见的做法,这样它们就可以像您期望的那样使用:

#define COPY( dest, x_copy, y_copy, z_copy ) \
    do { \
    dest->x = x_copy; \
    dest->y = y_copy; \
    dest->z = z_copy; \
    } while(0)

现在,替换您的宏,这个新宏会产生我们期望工作的输出:

#include <stdio.h>

#define COPY( dest, x_copy, y_copy, z_copy ) \
    do { \
    dest->x = x_copy; \
    dest->y = y_copy; \
    dest->z = z_copy; \
    } while(0)

int main( int argc, char* argv[] )
{
    int i;

    for(i=0;i<n;i++)
        COPY(a, b->x,b->y,b->z);

    for(i=0;i<n;i++)
        COPY(a, c->x,c->y,c->z);
}

同样,gcc -E main.c > main.i 导致从宏展开的正确代码:

int main( int argc, char* argv[] )
{
    int i;

    for(i=0;i<n;i++)
        do { a->x = b->x; a->y = b->y; a->z = b->z; } while(0);

    for(i=0;i<n;i++)
        do { a->x = c->x; a->y = c->y; a->z = c->z; } while(0);
}