c-preprocessor - 宏内的双重评估:使用 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()
。但我不确定标准对此有何规定。有保证吗 ?它依赖于实现吗?
解决方案
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());
推荐阅读
- css - Javascript:调整图像的宽度,保持比例,但如果需要,可以减少高度
- swift - 如何在协议中定义仅在协议的关联类型为特定类型时才需要实现的功能
- kubernetes - 通过 clusterIP 服务将 Pod 连接到 Pod?
- logging - LTTNG 是否可以进行日志轮换?
- python - 我无法通过 pycharm 的控制台运行代码
- php - 如何加入 laravel 关系
- amazon-web-services - 使用 pyspark (spark 2.4) 从 S3 读取 csv 作为 spark 数据帧
- python - 如何在 Dockerfile 中安装带有 miniconda 的软件包?
- android - 如何使用行 ID 将数据加载到详细活动中?
- javascript - 在 Acrobat JS API 中将透明颜色设置为 strokeColor 会导致黑色边框