首页 > 解决方案 > 宏内的双重评估:使用 sizeof() 来确定作为复合文字传递的数组大小

问题描述

C99 使得基本上可以在任何地方将数组定义为复合文字。

例如,给定一个sumf()接受数组float作为输入的普通函数,我们期望原型为: float sumf(const float* arrayf, size_t size);

然后可以这样使用: float total = sumf( (const float[]){ f1, f2, f3 }, 3 );

这很方便,因为不需要事先声明变量。语法有点难看,但这可以隐藏在宏后面。

但是,请注意最后的3. 这是数组的大小。它是必需的,以便sumf()知道在哪里停止。但是随着代码老化和重构,它也很容易产生错误,因为现在第二个参数必须与第一个参数定义保持同步。例如,添加f4需要将此值更新为 4,否则函数会返回错误的计算(并且没有警告通知此问题)。

所以最好保持两者同步。

如果它是通过变量声明的数组,那将很容易。我们可以有一个宏,它可以像这样简化表达式:float total = sumf( ARRAY(array_f) );只需#define ARRAY(a) (a) , sizeof(a) / sizeof(*(a)). 但是,array_f必须在调用函数之前定义,因此它不再是复合文字。

由于是复合字面量,所以没有名字,所以不能被引用。因此,我找不到比在两个参数中重复复合文字更好的方法了。

#define LIST_F(...)  (const float*)( __VA_ARGS__) , sizeof((const float*)( __VA_ARGS__)) / sizeof(float)
float total = sumf ( LIST_F( f1, f2, f3 ) );

这会奏效。将一个添加f4到列表中会自动将size参数更新为正确的大小。

但是,只要所有成员都是变量,这一切都可以正常工作。但是如果它是一个函数呢?该函数会被调用两次吗?比如说:float total = sumf ( LIST_F( v1, f2() ) );,会f2()被调用两次吗?f2()正如在 中提到的那样,这对我来说并不清楚sizeof(),因此理论上它可以在不实际调用的情况下知道返回类型的大小f2()。但我不确定标准对此有何规定。有保证吗 ?它依赖于实现吗?

标签: c-preprocessorsizeofc99compound-literals

解决方案


f2() 会被调用两次吗?

No, sizeof is not evaluated (unless it's a variable length array, but it's not).

what the standard says about that. Is there a guarantee ?

From C11 6.5.3.4p2:

The sizeof operator yields the size (in bytes) of its operand, [...] If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.

Is it implementation dependent ?

No, it should be always fine.

Note that your other macro uses (const float*)(__VA_ARGS__), that will not work - the syntax is (float[]){ stuff }. Anyway, I would just do one macro, why two, too much typing. Just:

#define SUMF_ARRAY(...)  \
     sumf( \
        (const float[]){__VA_ARGS__}, \
        sizeof((const float[]){__VA_ARGS__}) / sizeof(float))
float total = SUMF_ARRAY(f1(), f2(), f3());

推荐阅读