用C静态断言用C静态断言(Static assert in C)

2019-05-11 01:12发布

什么是实现编译时静态断言在C(不是C ++),特别强调GCC的最佳方式?

Answer 1:

C11标准增加了_Static_assert关键字。

这是因为GCC-4.6实现的 :

_Static_assert (0, "assert1"); /* { dg-error "static assertion failed: \"assert1\"" } */

第一时隙必须是一个积分常数表达式。 所述第二槽是一个常数字符串文字可以是长( _Static_assert(0, L"assertion of doom!")

我要指出,这也是在最近版本的铛的实现。



Answer 2:

这个工程在功能和非功能范围(而不是内部结构,联合)。

#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]

STATIC_ASSERT(1,this_should_be_true); 

int main()
{
 STATIC_ASSERT(1,this_should_be_true); 
}
  1. 如果编译时断言不能匹配,然后被GCC产生几乎可理解的消息sas.c:4: error: size of array 'static_assertion_this_should_be_true' is negative

  2. 宏可以或应该改变来产生typedef的一个唯一的名称(即串连__LINE__在结束static_assert_...名)

  3. 代替三元的,这可以被用于这样#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[2*(!!(COND))-1]这恰好甚至在生锈的奥尔德cc65工作(对于6502 CPU)编译器。

更新:为了完整起见,这里是与版本__LINE__

#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(!!(COND))*2-1]
// token pasting madness:
#define COMPILE_TIME_ASSERT3(X,L) STATIC_ASSERT(X,static_assertion_at_line_##L)
#define COMPILE_TIME_ASSERT2(X,L) COMPILE_TIME_ASSERT3(X,L)
#define COMPILE_TIME_ASSERT(X)    COMPILE_TIME_ASSERT2(X,__LINE__)

COMPILE_TIME_ASSERT(sizeof(long)==8); 
int main()
{
    COMPILE_TIME_ASSERT(sizeof(int)==4); 
}

UPDATE2:GCC特定代码

GCC 4.3(我猜)推出的“错误”和“警告”功能属性。 如果与该属性的函数的调用无法通过死代码消除(或其他措施)被淘汰,然后生成一个错误或警告。 这可以用来使编译时间与用户定义的故障的说明断言。 它仍然以确定他们如何能在命名空间范围内使用,而不诉诸一个虚拟函数:

#define CTC(X) ({ extern int __attribute__((error("assertion failure: '" #X "' not true"))) compile_time_check(); ((X)?0:compile_time_check()),0; })

// never to be called.    
static void my_constraints()
{
CTC(sizeof(long)==8); 
CTC(sizeof(int)==4); 
}

int main()
{
}

这是它的样子:

$ gcc-mp-4.5 -m32 sas.c 
sas.c: In function 'myc':
sas.c:7:1: error: call to 'compile_time_check' declared with attribute error: assertion failure: `sizeof(int)==4` not true


Answer 3:

CL

我知道这个问题明确提到的gcc,但只是为了完整性这里是Microsoft编译一个好办法。

使用负大小的数组类型定义不劝CL吐出一个体面的错误。 它只是说, error C2118: negative subscript 。 零宽度位字段票价在这方面更好。 因为这涉及typedeffing一个结构,我们真的需要使用唯一的类型名称。 __LINE__不削减芥末-它可能有一个COMPILE_TIME_ASSERT()在标头和源文件的同一行,你的编译将打破。 __COUNTER__就派上用场了(并且自4.3它一直在GCC)。

#define CTASTR2(pre,post) pre ## post
#define CTASTR(pre,post) CTASTR2(pre,post)
#define STATIC_ASSERT(cond,msg) \
    typedef struct { int CTASTR(static_assertion_failed_,msg) : !!(cond); } \
        CTASTR(static_assertion_failed_,__COUNTER__)

现在

STATIC_ASSERT(sizeof(long)==7, use_another_compiler_luke)

cl得出:

错误C2149:“static_assertion_failed_use_another_compiler_luke”:命名位字段不能具有零宽度

海湾合作委员会也给出了一个可理解的消息:

误差:零宽度位字段“static_assertion_failed_use_another_compiler_luke”



Answer 4:

从维基百科 :

#define COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;}

COMPILE_TIME_ASSERT( BOOLEAN CONDITION );


Answer 5:

如果使用与STATIC_ASSERT()宏__LINE__ ,能够避免在.c文件的条目,并通过包括在头文件中不同的入口之间的行号的冲突__INCLUDE_LEVEL__

例如 :

/* Trickery to create a unique variable name */
#define BOOST_JOIN( X, Y )      BOOST_DO_JOIN( X, Y )
#define BOOST_DO_JOIN( X, Y )   BOOST_DO_JOIN2( X, Y )
#define BOOST_DO_JOIN2( X, Y )  X##Y
#define STATIC_ASSERT(x)        typedef char \
        BOOST_JOIN( BOOST_JOIN(level_,__INCLUDE_LEVEL__), \
                    BOOST_JOIN(_assert_on_line_,__LINE__) ) [(x) ? 1 : -1]


Answer 6:

建议使用采用该解决方案typedef

#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]

与阵列声明typedef关键字,不能保证在编译时进行评估。 例如,在块范围下面的代码将编译:

int invalid_value = 0;
STATIC_ASSERT(invalid_value, this_should_fail_at_compile_time_but_will_not);

我反而建议内容(在C99):

#define STATIC_ASSERT(COND,MSG) static int static_assertion_##MSG[(COND)?1:-1]

由于的static参数,则阵列将在编译时定义。 请注意,这个断言将只与工作COND其在编译时进行评估。 它不会(即编译会失败)与基于存储器中的值,如分配给变量的值的条件下工作。



Answer 7:

经典方法是使用阵列:

char int_is_4_bytes_assertion[sizeof(int) == 4 ? 1 : -1];

它的工作原理,因为如果断言是真正的数组的大小为1,这是有效的,但如果是假-1的尺寸给出了一个编译错误。

大多数编译器将显示该变量并指向代码,您可以留下有关断言最终注释权部分的名称。



Answer 8:

因为:

  1. _Static_assert()现在在GCC定义对于C的所有版本,并
  2. static_assert()是在C ++ 11和后面定义

以下简单的宏STATIC_ASSERT()因此工作在:

  1. C ++:
    1. C ++ 11( g++ -std=c++11 )或更高
  2. C:
    1. gcc -std=c90
    2. gcc -std=c99
    3. gcc -std=c11
    4. gcc (未指定STD)

定义STATIC_ASSERT如下:

/* For C++: */
#ifdef __cplusplus
    #ifndef _Static_assert
        #define _Static_assert static_assert /* `static_assert` is part of C++11 or later */
    #endif
#endif
/* Now for gcc (C) (and C++, given the define above): */
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") failed")

现在使用它:

STATIC_ASSERT(1 > 2); // Output will look like: error: static assertion failed: "(1 > 2) failed" 

例子:

使用gcc 4.8.4测试过在Ubuntu:

实施例1:良好gcc输出(即: STATIC_ASSERT()代码的工作,但条件是假的,引起一个编译时断言):

$ GCC -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c:在函数“主”
static_assert.c:78:38:错误:静态断言失败: “(1> 2)失败”
#定义STATIC_ASSERT(test_for_true)_Static_assert((test_for_true), “(” #test_for_true “)失败”)
^
static_assert.c:88:5:注:在宏 'STATIC_ASSERT' 的膨胀
STATIC_ASSERT(1> 2);
^

实施例2:g++ -std=c++11输出(即: STATIC_ASSERT()代码的工作,但条件是假的,引起一个编译时断言):

$克++ -Wall -std = C ++ 11 -o static_assert static_assert.c && ./static_assert
static_assert.c:在函数“诠释主()”
static_assert.c:74:32:错误:静态断言失败:(1> 2)失败
的#define _Static_assert static_assert / * static_assert是C ++ 11或后面部分* /
^
static_assert.c:78:38:注意:在宏观扩张 '_Static_assert'
#定义STATIC_ASSERT(test_for_true)_Static_assert((test_for_true), “(” #test_for_true “)失败”)
^
static_assert.c:88:5:注:在宏 'STATIC_ASSERT' 的膨胀
STATIC_ASSERT(1> 2);
^

实施例3: 失败 C ++输出(即:断言码不能正常工作在所有,因为这是使用 C ++ 11的一个版本的C ++):

$ G ++ -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c:88:5:警告:标识符 'static_assert' 是在C ++ 11 [-Wc ++ 0x中-COMPAT]关键字
STATIC_ASSERT(1> 2);
^
static_assert.c:在函数“诠释主()”
static_assert.c:78:99:错误: 'static_assert' 在此范围未声明
#定义STATIC_ASSERT(test_for_true)_Static_assert((test_for_true), “(” #test_for_true “)失败”)
^
static_assert.c:88:5:注:在宏 'STATIC_ASSERT' 的膨胀
STATIC_ASSERT(1> 2);
^

全部测试结果在这里:

/*
static_assert.c
- test static asserts in C and C++ using gcc compiler

Gabriel Staples
4 Mar. 2019 

To be posted in:
1. https://stackoverflow.com/questions/987684/does-gcc-have-a-built-in-compile-time-assert/987756#987756
2. https://stackoverflow.com/questions/3385515/static-assert-in-c/7287341#7287341

To compile & run:
  C:
    gcc -Wall -o static_assert static_assert.c && ./static_assert
    gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert
    gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert
    gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert
  C++:
    g++ -Wall -o static_assert static_assert.c && ./static_assert
    g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert
    g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert
    g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert

-------------
TEST RESULTS:
-------------

1. `_Static_assert(false, "1. that was false");` works in:
  C:
    gcc -Wall -o static_assert static_assert.c && ./static_assert             YES
    gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert    YES
    gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert    YES
    gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert    YES
  C++:
    g++ -Wall -o static_assert static_assert.c && ./static_assert             NO
    g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert  NO

2. `static_assert(false, "2. that was false");` works in:
  C:
    gcc -Wall -o static_assert static_assert.c && ./static_assert             NO
    gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert    NO
    gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert    NO
    gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert    NO
  C++:
    g++ -Wall -o static_assert static_assert.c && ./static_assert             NO
    g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert  YES

3. `STATIC_ASSERT(1 > 2);` works in:
  C:
    gcc -Wall -o static_assert static_assert.c && ./static_assert             YES
    gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert    YES
    gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert    YES
    gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert    YES
  C++:
    g++ -Wall -o static_assert static_assert.c && ./static_assert             NO
    g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert  YES

*/

#include <stdio.h>
#include <stdbool.h>

/* For C++: */
#ifdef __cplusplus
    #ifndef _Static_assert
        #define _Static_assert static_assert /* `static_assert` is part of C++11 or later */
    #endif
#endif
/* Now for gcc (C) (and C++, given the define above): */
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") failed")


int main(void)
{
    printf("Hello World\n");

    /*_Static_assert(false, "1. that was false");*/
    /*static_assert(false, "2. that was false");*/

    STATIC_ASSERT(1 > 2);

    return 0;
}


Answer 9:

对于那些你想要的东西非常基本和便携,但没有进入C ++ 11的特点,我刚写的东西。
使用STATIC_ASSERT正常(你可以在同一个函数写两次,如果你想),并使用GLOBAL_STATIC_ASSERT的功能之外具有独特的短语作为第一个参数。

#if defined(static_assert)
#   define STATIC_ASSERT static_assert
#   define GLOBAL_STATIC_ASSERT(a, b, c) static_assert(b, c)
#else
#   define STATIC_ASSERT(pred, explanation); {char assert[1/(pred)];(void)assert;}
#   define GLOBAL_STATIC_ASSERT(unique, pred, explanation); namespace ASSERTATION {char unique[1/(pred)];}
#endif

GLOBAL_STATIC_ASSERT(first, 1, "Hi");
GLOBAL_STATIC_ASSERT(second, 1, "Hi");

int main(int c, char** v) {
    (void)c; (void)v;
    STATIC_ASSERT(1 > 0, "yo");
    STATIC_ASSERT(1 > 0, "yo");
//    STATIC_ASSERT(1 > 2, "yo"); //would compile until you uncomment this one
    return 0;
}

说明:
首先,如果你有真正的断言,你就一定要使用,如果它是可用它检查。
如果不这样做就断言通过让您的pred icate,并自行将它。 这做了两两件事。
如果它是零,ID EST,断言失败,它会零错误导致除(算术被迫,因为它试图声明数组)。
如果不是零,它正常化数组大小到1 因此,如果断言过去了,你不希望因为你的谓语评估,它终将以失败告终-1 (无效),或者是232442 (空间大量浪费,IDK它是否会被优化掉了)。
对于STATIC_ASSERT它被包裹在括号,这使得它的模块,其作用域变量assert ,这意味着你可以写了很多次。
它也蒙上它void ,这是摆脱已知的方式unused variable警告。
对于GLOBAL_STATIC_ASSERT ,而不是在一个码块时,它产生一个名称空间。 命名空间允许的功能外。 一个unique的标识符需要停止任何冲突的定义,如果你使用这一个不止一次。


工作对我来说在GCC和VS'12 C ++



Answer 10:

这工作,用“删除未使用的”选项集。 我可以用一个全局函数来检查的全局参数。

//
#ifndef __sassert_h__
#define __sassert_h__

#define _cat(x, y) x##y

#define _sassert(exp, ln) \
extern void _cat(ASSERT_WARNING_, ln)(void); \
if(!(exp)) \
{ \
    _cat(ASSERT_WARNING_, ln)(); \
}

#define sassert(exp) _sassert(exp, __LINE__)

#endif //__sassert_h__

//-----------------------------------------
static bool tab_req_set_relay(char *p_packet)
{
    sassert(TXB_TX_PKT_SIZE < 3000000);
    sassert(TXB_TX_PKT_SIZE >= 3000000);
    ...
}

//-----------------------------------------
Building target: ntank_app.elf
Invoking: Cross ARM C Linker
arm-none-eabi-gcc ...
../Sources/host_if/tab_if.c:637: undefined reference to `ASSERT_WARNING_637'
collect2: error: ld returned 1 exit status
make: *** [ntank_app.elf] Error 1
//


Answer 11:

这个工作对一些旧的gcc。 对不起,我忘了是什么版本:

#define _cat(x, y) x##y

#define _sassert(exp, ln)\
extern char _cat(SASSERT_, ln)[1]; \
extern char _cat(SASSERT_, ln)[exp ? 1 : 2]

#define sassert(exp) _sassert((exp), __LINE__)

//
sassert(1 == 2);

//
#148 declaration is incompatible with "char SASSERT_134[1]" (declared at line 134)  main.c  /test/source/controller line 134    C/C++ Problem


文章来源: Static assert in C