不同情况下C宏的不同行为

时间:2015-07-18 18:22:11

标签: c

这是代码,

#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。但即使我提供真实条件,第二次使用宏也不会抛出编译错误。我无法理解这种行为。有人可以解释一下吗?

3 个答案:

答案 0 :(得分:5)

AuthenticatesUsers宏旨在实现编译时断言。

给定一个可以在编译时计算的参数,如果参数为非零(true),则会导致编译时失败,如果参数为非零(false),则不执行任何操作。

它不适用于在运行时计算的参数。

BUILD_BUG_ON

#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) 是两个逻辑&#34;不是&#34;运营商;它们可以将!!的值标准化为0,将任何非零值标准化为0

如果结果条件为1(真),则1的值为1 - 2*!!(condition)。如果条件为-1(false),则值为0

数组可能没有负(或零)大小。一些编译器可能支持零长度数组作为扩展;这个宏确保即使是这样的编译器也能诊断出错误。如果size是常量表达式,则具有负大小的数组是违反约束的,需要编译时诊断。

如果表达式为false,则没有错误;宏扩展为一个什么都不做的表达式。如果表达式为真并且是常量表达式,则宏的扩展会尝试定义负数大小的数组,从而导致编译时错误。

如果表达式不是常量,则宏不起作用。 C(C99及更高版本)允许可变长度数组(VLA)。不允许零或负长度的VLA,但是通常不能在编译时检测到定义这样的VLA。这是未定义的行为 - 在这种情况下,它可能什么也不做。 (只是为了使文件范围内不允许使用VLA。)

理想情况下,宏应附有解释如何使用它的文档。该文档应该解释该参数必须是编译时表达式。

底线:您应该只将此宏与常量表达式参数一起使用。 (要测试运行时表达式,可以使用1。)如果使用值为零的非常量表达式,则行为未定义;最可能的结果是预期的&#34;断言&#34;不会触发,也不会检测到错误。

答案 1 :(得分:1)

我认为你的方法完全不自然 这是糟糕的代码,即使它会起作用 您可以尝试更自然的方法来处理错误。

断言

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编译器指令,但在这种情况下我不建议你,因为sizeof不理解#if

语法为:

#if some_condition // Put an integer constant expression readable by an #if.
#  error This is wrong.
#endif

这三种方法中的一些必须满足您的需求。

答案 2 :(得分:0)

更改

BUILD_BUG_ON(cond);

BUILD_BUG_ON(1);

获得预期的行为。

cond值仅在运行时可用时,将在未优化的构建中生成计算所需sizeof的代码(并将其结果丢弃),或者在优化时完全忽略整个表达式生成。