宏内的双重评估:sizeof() 的一个案例,用于确定作为复合文字传递的数组大小

Double evaluation within macro: a case of sizeof() to determine array's size passed as compound literal

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

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

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

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

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

所以最好让两者保持同步。

如果是通过变量声明的数组,就容易了。 我们可以有一个宏,它可以简化这样的表达式: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()会被调用两次吗?这对我来说不清楚,因为 sizeof() 中提到了 f2(),因此理论上它可以在不实际调用 f2() 的情况下知道 return 类型大小。但我不确定标准是怎么说的。有保证吗 ?它取决于实现吗?

will f2() be invoked twice?

不,sizeof不求值(除非是变长数组,但不是)。

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

来自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 ?

不,应该总是好的。

请注意,您的其他宏使用 (const float*)(__VA_ARGS__),这将不起作用 - 语法为 (float[]){ stuff }。无论如何,我只会做一个宏,为什么要做两个,打字太多了。只是:

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