[英]Different behaviour of C macro for different cases
这是代码,
#include<stdio.h>
#include<stdbool.h>
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
struct my_struct {
int a, b;
// char c;
};
int main() {
bool cond = 1;
BUILD_BUG_ON((sizeof(struct my_struct) % 8) != 0);
BUILD_BUG_ON(cond);
return 0;
}
第一次使用BUILD_BUG_ON(条件)宏如果sizeof struct不等于8则抛出编译错误,因为条件将评估为true。 但即使我提供真实条件,第二次使用宏也不会抛出编译错误。 我无法理解这种行为。 谁能解释一下?
BUILD_BUG_ON
在实现编译时断言。
给定一个可以在编译时计算的参数,如果参数为非零(true),则会导致编译时失败,如果参数为非零(false)则不会执行任何操作。
它不适用于在运行时计算的参数。
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
!!
是两个合乎逻辑的“非”运营商; 它们具有将值0
归一化为0
,将任何非零值归一化为1
。
如果结果条件为1
(真),则1 - 2*!!(condition)
值为-1
。 如果条件为0
(假),则值为1
。
数组可能没有负(或零)大小。 一些编译器可能支持零长度数组作为扩展; 这个宏确保即使是这样的编译器也能诊断出错误。 如果size是常量表达式,则具有负大小的数组是违反约束的,需要编译时诊断。
如果表达式为false,则没有错误; 宏扩展为一个什么都不做的表达式。 如果表达式为true 且是常量表达式 ,则宏的扩展尝试定义负大小的数组,从而导致编译时错误。
如果表达式不是常量,则宏不起作用。 C(C99和更高版本)允许可变长度数组 (VLA)。 不允许零或负长度的VLA,但是通常不能在编译时检测到定义这样的VLA。 它是未定义的行为 - 在这种情况下,它可能什么都不做。 (只是为了使文件范围内不允许使用VLA。)
理想情况下,宏应附有解释如何使用它的文档。 该文档应该解释该参数必须是编译时表达式。
底线:您应该只将此宏与常量表达式参数一起使用。 (要测试运行时表达式,可以使用assert()
。)如果使用值为零的非常量表达式,则行为未定义; 最可能的结果是预期的“断言”不会触发,并且不会检测到错误。
我认为你的方法完全不自然。
这是糟糕的代码,即使它会起作用。
您可以尝试更自然的方法来处理错误。
断言
宏assert
在运行时测试条件,并在测试失败时显示错误消息。
#include <assert.h>
int main(void)
{
assert((sizeof(struct my_struct) % 8) != 0);
}
static_assert
在C11投诉编译器中, static_assert(condition, "Error message")
执行常量表达式所需的操作:
static_assert((sizeof(struct my_struct) % 8) != 0, "Wrong struct");
#if和#error
也可以使用#if
和#error
编译器指令,但在这种情况下我不建议你,因为#if
不理解sizeof
。
语法是:
#if some_condition // Put an integer constant expression readable by an #if.
# error This is wrong.
#endif
这三种方法中的一些必须满足您的需求。
更改
BUILD_BUG_ON(cond);
至
BUILD_BUG_ON(1);
获得预期的行为。
当cond
值仅在运行时可用时,将在未优化的构建中生成计算所需sizeof
的代码(并将其结果丢弃),或者在优化的构建中完全忽略整个表达式。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.