【问题标题】:Putting loop inside C macro将循环放在 C 宏中
【发布时间】:2013-07-25 19:04:59
【问题描述】:

我正在寻找一种将以下函数结构转换为宏的方法。我知道,这是一个愚蠢且毫无意义的例子,但它说明了这一点,因为我无法给出我的实际源代码。

int foo(int x, int y)
{
  do
  {
    --x;
    ++y;
  }while(x > y);

  return x * y; //note that x and y have changed values here.
}

这样我就可以在 main 或其他函数中调用该函数,如下所示:

int next_x = foo(x,y);

我似乎无法在这里得到 100% 正确的语法。这是我糟糕的尝试:

#define FOO(x,y)      \
(                     \
  do                  \
  {                   \
    --x;              \
    ++y;              \
  }while(x < y),      \
  x                   \
)                     

最后 x 的原因是,理论上我可以做到这一点

int next_x = FOO(x,y);

但是,我得到一个语法错误,我不知道为什么。任何帮助将不胜感激。

================================================

附加信息

我还应该注意,我还有其他相应结构的宏:

#define INIT(x,y)   
(   
  x = //something,
  y = //something
)

#define NEXT_INT(x,y)    \
(                        \
  INIT(x,y),             \
  get_next_num(x,y)      \            //Note, this is an inline function call , not a macro.
 )

#define NEXT_FLOAT(x,y,temp)         \
(                                    \
  temp = NEXT_INT(x,y),              \
  temp ? temp * 1.23456 : FLT_MIN    \
)                                    

所以,我可以并且已经做了以下事情:

float my_flt = NEXT_FLOAT(x,y,temp);

【问题讨论】:

  • 我对宏的模糊记忆告诉我它们需要全部在一行上。
  • 这里没有真正好的答案,这可能很好。否则人们会认为这是一个好主意,并且会更频繁地这样做。您的 C 编译器是否不支持“内联”(我认为它符合 C99 标准)?我能看到这样做的唯一原因是创建 C++ 模板的宏版本。请不要。一些可怜的维持工程师会拿你的生命合同。
  • 避免内联函数的目的是什么?
  • @audiFanatic:您假设内联函数的性能很差,但我不清楚您是否真的在系统上看到了问题。除非您有不允许在编译器上启用优化的要求(或编译器产生质量很差的优化),否则出于您陈述的原因,没有必要更喜欢宏而不是内联函数。
  • @RossPresser 不,你可以用 \ 告诉计算机“下一行仍然是 marco 的一部分”

标签: c syntax do-while c-preprocessor


【解决方案1】:

C 语法只允许用逗号运算符 (,) 分隔表达式。 do ... while() 不是表达式,而是语句,因此将其用作逗号运算符的值是错误的。

一般来说,内联函数应该优先于宏来执行一些内联​​计算。它们更容易实现、更不容易出错并且更容易维护。在极少数情况下,内联函数会失败而宏会成功。

确实没有一个安全的宏来实现您的目标,但一种解决方法是将您要更新的变量作为宏参数传递。

#define FOO(x, y, result) \
    do { \
        do { \
            --x; \
            ++y; \
        } while(x > y); \
        result = x * y; \
    } while(0)

如果您使用的是 GCC,您可以使用他们的 statement-expression syntax,它是 C 的扩展,而不是标准的 C 功能。语法如下:

({ statement; statement; expression; })

上面的结果将是最后一个表达式。


在评论中,你表达:

我需要保存xy 的新值,以便后续调用此函数foo.. 这样做的明显方法是将指针传递给xy,然后只需制作一个常规的内联函数。但是我不想这样做,因为我正在制作一个时间紧迫的应用程序,我需要 xy 保留在寄存器中。

您假设xy 不会留在寄存器中,这是错误的假设。这取决于编译器优化代码的能力的质量。考虑以下几点:

static inline int foo (int *x, int *y) {
    do {
        --*x;
        ++*y;
    } while (*x > *y);
    return *x**y;
}

int main (int argc, char *argv[]) {
    int x = argc+1;
    int y = argc;
    foo(&x, &y);
    return 0;
}

使用gcc -O1编译时,结果为:

main:
.LFB14:
        movl    %edi, %edx
.L2:
        movl    %edi, %eax
        addl    $1, %edx
        subl    $1, %edi
        cmpl    %edx, %eax
        jg      .L2
        movl    $0, %eax
        ret
.LFE14:

您会发现没有指针值取消引用,这正是您想要发生的。

【讨论】:

  • 嗯...这很有趣。我想我是这么认为的.但是查看我的 .asm 文件,您似乎是对的,尽管它与其他代码交织在一起,因此很难判断。
  • @audiFanatic:这取决于函数是否被内联。如果该函数不是内联函数,它可能会受益于从指针中复制值然后再将其复制回来。但是对于内联函数,大多数编译器都会做正确的事情。
【解决方案2】:

您不能将do/while 循环用作表达式,因此您需要在括号内使用块,如下所示:

编辑:这是一个 GCC 扩展,而不是标准 C

#define FOO(x,y)    \
({                  \
  do                \
  {                 \
    --x;            \
    ++y;            \
  }while(x > y);    \
  x;                \
})

【讨论】:

  • 我的回答中的其他错误是缩进很差,对于 GCC,我没有意识到这一点。很好的回答德鲁麦克。谢谢!
  • 否则我们可以在`\`后面留一个空格。我认为最常见的错误
  • 这可能是个问题,因为我没有使用 GCC,我正在为我的应用程序使用专门的编译器。不过我很好奇,为什么将括号放在x; 之后而不是仅在循环周围?
  • 这就是语法的工作原理。很可能它表明它是一个按顺序执行的代码块,括号告诉编译器使用最后一条语句作为表达式的结果。
【解决方案3】:

这里的一个问题是多行宏需要在每行的末尾有一个\

【讨论】:

  • 对不起,我在notepad ++中很快就完成了,我忘了添加它们,我的错。但不,不幸的是,这不是问题。
猜你喜欢
  • 2016-03-04
  • 1970-01-01
  • 1970-01-01
  • 2015-03-29
  • 2014-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多