是否可以在#define子句中的函数中声明变量

时间:2017-02-21 15:57:32

标签: c c-preprocessor

为了加快程序的性能,我想介绍一个计算浮点除法的剩余函数的函数(显然,商是自然的。)

因此我有以下简单的功能:

double mfmod(double x,double y) {
  double a;
  return ((a=x/y)-(int)a)*y;
  }

正如我所听到的那样,通过将此函数放在#define子句中,我可以加快速度,但变量a使得这很难。此刻我在这里:

#define mfmod(x,y) {         \
  double a;                  \
  return ((a=x/y)-(int)a)*y; \
  }

但是由于变量,尝试启动它会产生问题。

问题如下:我正在尝试启动此功能:

double test = mfmod(f,div);

由于错误消息type name is not allowed,无法编译。 (为了您的信息,fdivdoubles

有人知道怎么做吗? (如果它甚至可能)(我正在使用Windows,更确切地说是Visual Studio,而不是GCC

3 个答案:

答案 0 :(得分:2)

  

正如我所听到的那样,通过将此函数置于#define子句中,我可以加快速度

我认为你一定有误解。当然,建议是将行为实现为 a(#define d)宏,而不是作为函数。您的宏在语法上是有效的,但扩展它的代码不适合调用您的函数。

将此定义为宏基本上是手动内联函数体的一种方法,但它有一些缺点。特别是,在标准C中,必须扩展为表达式的宏(即可用作值的宏)不能包含代码块,因此不能包含变量声明。反过来,这可能使得无法避免对宏观参数的多重评估,这是一个大问题。

以下是编写宏的一种方法:

#define mfmod(x,y) ( ((x)/(y)) - (int) ((x)/(y)) )

然而,由于它的行为因参数类型而异,因此它不能明确胜过函数,它必须两次计算两个参数(在某些情况下会产生意外甚至未定义的结果),并且它还必须执行师两次。

如果您愿意更改使用模式以便宏设置结果变量而不是扩展到表达式,那么您可以解决许多问题。 @BnBDim为此提供了第一次切割,但它遇到了与上述相同类型和多重评估问题。以下是如何使用它来获得与函数相同的结果:

#define mfmod(x, y, res) do {           \
    double _div  = (y);                 \
    double _quot = (double) (x) / _div; \
    res = (_quot - (int) _quot) * _div; \
} while (0)

请注意,每次引用一次参数,并且在括号内引用参数,但res必须是左值。您可以像使用void函数一样使用它,而不是像返回值函数一样:

double test;
mfmod(f, div, test);

如果宏的一个实际参数与它提供的代码块中声明的变量之一发生冲突,那么仍然会提供一个小的,但不可避免的破损风险。使用带有下划线的变量名称旨在最大限度地降低风险。

总的来说,我倾向于使用该函数,并让编译器处理内联。如果你想鼓励编译器这样做,那么你可以考虑声明函数inline,但很可能它不需要这样的提示,并且它没有义务兑现它。

或者更好的是,只要使用fmod(),除非您确定这样做构成了瓶颈。

答案 1 :(得分:1)

回答上述问题:是的,您可以在已定义的宏函数中声明变量'就像你正在使用的那种,在某些情况下。其他一些答案已经展示了如何做到这一点的例子。

当前#define的问题在于,您在代码中间告诉return某些内容,并且您没有按照您可能想要的方式扩展宏。如果我像这样使用你的宏:

...
double y = 1.0;
double x = 1.0;
double z = mfmod(x, y);
int other = (int)z - 1;
...

这将扩展为:

...
double y = 1.0;
double x = 1.0;
double z = {
  double a;
  return ((a=x/y)-(int)a)*y;
};
int other = (int)z - 1;
...

该函数(如果已编译)将永远不会超出z的初始化,因为它将在宏的中间返回。你也试图分配' z的代码块。

话虽如此,这是在没有任何(陈述的)基准测试的情况下对性能做出假设的另一个例子。您是否实际使用内联函数测量了任何性能问题?

__attribute__((const))
extern inline double mfmod(const double x, const double y) {
  const double a = x/y;
  return (a - (int)a) * y;
}

这不仅比宏更清晰,更清晰,更容易调试,它还具有使用const属性声明的额外好处,这将向编译器建议后续调用函数相同的参数应返回相同的值,这可能导致对函数的重复调用完全被优化掉,而宏(每次都会在概念上进行评估)。老实说,即使使用本地double来缓存除法结果也可能是一个不成熟的优化,因为编译器可能会优化它。如果这是我,我绝对有一个宏来做这个,我会写如下:

#define mfmod(x, y) (((x/y)-((int)(x/y)))*y)

几乎肯定没有任何明显的性能影响在优化下进行两次分裂。如果有,我会使用上面的内联函数。我会留给你做基准测试。

答案 2 :(得分:0)

您可以使用此解决方法

#define mfmod(x,y,res)       \
  do {                       \
      double a=(x)/(y);      \
      res = (a-(int)a)*(y);  \
  } while(0)