C语言 C中的静态断言
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3385515/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Static assert in C
提问by Matt Joiner
What's the best way to achieve compile time static asserts in C (not C++), with particular emphasis on GCC?
在 C(不是 C++)中实现编译时静态断言的最佳方法是什么,特别强调 GCC?
回答by Nordic Mainframe
This works in function and non-function scope (but not inside structs,unions).
这适用于函数和非函数作用域(但不适用于结构体、联合体)。
#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);
}
If the compile time assertion could not be matched, then an almost intelligible message is generated by GCC
sas.c:4: error: size of array ‘static_assertion_this_should_be_true' is negativeThe macro could or should be changed to generate a unique name for the typedef (i.e. concatenate
__LINE__at the end of thestatic_assert_...name)Instead of a ternary, this could be used as well
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[2*(!!(COND))-1]which happens to work even on the rusty olde cc65 (for the 6502 cpu) compiler.
如果编译时断言无法匹配,则 GCC 会生成几乎可理解的消息
sas.c:4: error: size of array ‘static_assertion_this_should_be_true' is negative宏可以或应该更改为 typedef 生成唯一名称(即
__LINE__在static_assert_...名称末尾连接)代替三元,这也可以使用,
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[2*(!!(COND))-1]即使在生锈的 olde cc65(用于 6502 cpu)编译器上也可以使用。
UPDATE:For completeness sake, here's the version with __LINE__
更新:为了完整起见,这里是版本__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 specific code
UPDATE2:GCC 特定代码
GCC 4.3 (I guess) introduced the "error" and "warning" function attributes. If a call to a function with that attribute could not be eliminated through dead code elimination (or other measures) then an error or warning is generated. This can be used to make compile time asserts with user defined failure descriptions. It remains to determine how they can be used in namespace scope without resorting to a dummy function:
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()
{
}
And this is how it looks like:
这就是它的样子:
$ 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
回答by emsr
C11 standard adds the _Static_assertkeyword.
C11标准增加了_Static_assert关键字。
This is implemented since gcc-4.6:
_Static_assert (0, "assert1"); /* { dg-error "static assertion failed: \"assert1\"" } */
The first slot needs to be an integral constant expression. The second slot is a constant string literal which can be long (_Static_assert(0, L"assertion of doom!")).
第一个槽需要是一个整数常量表达式。第二个槽是一个常量字符串文字,它可以很长 ( _Static_assert(0, L"assertion of doom!"))。
I should note that this is also implemented in recent versions of clang.
我应该注意,这也在最新版本的 clang 中实现。
回答by bobbogo
cl
CL
I know the question explicitly mentions gcc, but just for completeness here is a tweak for Microsoft compilers.
我知道这个问题明确提到了 gcc,但为了完整起见,这里是对 Microsoft 编译器的调整。
Using the negatively sized array typedef does not persuade clto spit out a decent error. It just says error C2118: negative subscript. A zero-width bitfield fares better in this respect. Since this involves typedeffing a struct, we really need to use unique type names. __LINE__does not cut the mustard — it is possible to have a COMPILE_TIME_ASSERT()on the same line in a header and a source file, and your compile will break. __COUNTER__comes to the rescue (and it has been in gcc since 4.3).
使用负大小的数组 typedef 并不能说服cl吐出一个不错的错误。它只是说error C2118: negative subscript。零宽度位域在这方面表现更好。由于这涉及对结构进行类型定义,因此我们确实需要使用唯一的类型名称。__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__)
Now
现在
STATIC_ASSERT(sizeof(long)==7, use_another_compiler_luke)
under clgives:
下面cl给出:
error C2149: 'static_assertion_failed_use_another_compiler_luke' : named bit field cannot have zero width
错误 C2149:“static_assertion_failed_use_another_compiler_luke”:命名位字段不能有零宽度
Gcc also gives an intelligible message:
Gcc 还给出了一个可理解的信息:
error: zero width for bit-field ‘static_assertion_failed_use_another_compiler_luke'
错误:位域“static_assertion_failed_use_another_compiler_luke”的宽度为零
回答by Tyler
回答by FredFredFredFred
I would NOTrecommend using the solution using a typedef:
我不建议使用以下解决方案typedef:
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]
The array declaration with typedefkeyword is NOT guaranteed to be evaluated at compile time. For example, the following code in block scope will compile:
typedef不保证在编译时评估带有关键字的数组声明。例如,块作用域中的以下代码将编译:
int invalid_value = 0;
STATIC_ASSERT(invalid_value, this_should_fail_at_compile_time_but_will_not);
I would recommend this instead (on C99):
我会推荐这个(在 C99 上):
#define STATIC_ASSERT(COND,MSG) static int static_assertion_##MSG[(COND)?1:-1]
Because of the statickeyword, the array will be defined at compile time. Note that this assert will only work with CONDwhich are evaluated at compile time. It will not work with (i.e. the compile will fail) with conditions that are based on values in memory, such as values assigned to variables.
由于static关键字,数组将在编译时定义。请注意,此断言仅适用COND于在编译时评估的内容。它不适用于(即编译将失败)基于内存中的值的条件,例如分配给变量的值。
回答by BrentNZ
If using the STATIC_ASSERT() macro with __LINE__, it is possible to avoid line number clashes between an entry in a .c file and a different entry in a header file by including __INCLUDE_LEVEL__.
如果将 STATIC_ASSERT() 宏与 一起使用__LINE__,则可以通过包含 来避免 .c 文件中的条目与头文件中的不同条目之间的行号冲突__INCLUDE_LEVEL__。
For example :
例如 :
/* 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]
回答by Gabriel Staples
Because:
因为:
_Static_assert()is now defined in gcc for all versions of C, andstatic_assert()is defined in C++11 and later
_Static_assert()现在在 gcc 中为所有版本的 C 定义,并且static_assert()在 C++11 及更高版本中定义
The following simple macro for STATIC_ASSERT()therefore works in:
因此,以下简单的宏STATIC_ASSERT()适用于:
- C++:
- C++11 (
g++ -std=c++11) or later
- C++11 (
- C:
gcc -std=c90gcc -std=c99gcc -std=c11gcc(no std specified)
- C++:
- C++11 (
g++ -std=c++11) 或更高版本
- C++11 (
- C:
gcc -std=c90gcc -std=c99gcc -std=c11gcc(未指定标准)
Define STATIC_ASSERTas follows:
定义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")
Now use it:
现在使用它:
STATIC_ASSERT(1 > 2); // Output will look like: error: static assertion failed: "(1 > 2) failed"
Examples:
例子:
Tested in Ubuntu using gcc 4.8.4:
使用 gcc 4.8.4 在 Ubuntu 中测试:
Example 1:good gccoutput (ie: the STATIC_ASSERT()codes works, but the condition was false, causing a compile-time assert):
示例 1:良好的gcc输出(即:STATIC_ASSERT()代码有效,但条件为假,导致编译时断言):
$ gcc -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c: In function ‘main'
static_assert.c:78:38: error: static assertion failed: "(1 > 2) failed"
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") failed")
^
static_assert.c:88:5: note: in expansion of macro ‘STATIC_ASSERT'
STATIC_ASSERT(1 > 2);
^
$ gcc -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c: 在函数 'main'
static_assert.c:78:38: 错误:静态断言失败:“(1 > 2) failed”
#define STATIC_ASSERT(test_for_true ) _Static_assert((test_for_true), "(" #test_for_true ") failed")
^
static_assert.c:88:5: 注意:在宏 'STATIC_ASSERT'
STATIC_ASSERT(1 > 2) 的扩展中;
^
Example 2:good g++ -std=c++11output (ie: the STATIC_ASSERT()codes works, but the condition was false, causing a compile-time assert):
示例 2:良好的g++ -std=c++11输出(即:STATIC_ASSERT()代码有效,但条件为假,导致编译时断言):
$ g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert
static_assert.c: In function ‘int main()'
static_assert.c:74:32: error: static assertion failed: (1 > 2) failed
#define _Static_assert static_assert /*static_assertis part of C++11 or later */
^
static_assert.c:78:38: note: in expansion of macro ‘_Static_assert'
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") failed")
^
static_assert.c:88:5: note: in expansion of macro ‘STATIC_ASSERT'
STATIC_ASSERT(1 > 2);
^
$ g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert
static_assert.c: 在函数 'int main()'
static_assert.c:74:32: 错误: 静态断言失败: (1 > 2) 失败
#define _Static_assert static_assert /*static_assert是 C++11 或更高版本的一部分 */
^
static_assert.c:78:38: 注意:在宏 '_Static_assert' 的扩展中
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") failed")
^
static_assert.c:88:5: 注意:在宏 'STATIC_ASSERT'
STATIC_ASSERT(1 > 2) 的扩展中;
^
Example 3:failedC++ output (ie: the assert code doesn't work properly at all, since this is using a version of C++ beforeC++11):
示例 3:失败的C++ 输出(即:断言代码根本无法正常工作,因为它使用的是C++11之前的C++版本):
$ g++ -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c:88:5: warning: identifier ‘static_assert' is a keyword in C++11 [-Wc++0x-compat]
STATIC_ASSERT(1 > 2);
^
static_assert.c: In function ‘int main()'
static_assert.c:78:99: error: ‘static_assert' was not declared in this scope
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") failed")
^
static_assert.c:88:5: note: in expansion of macro ‘STATIC_ASSERT'
STATIC_ASSERT(1 > 2);
^
$ 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: 在函数 'int main()'
static_assert.c:78:99: 错误:'static_assert' 未在此范围内声明
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true " ) 失败")
^
static_assert.c:88:5: 注意:在宏 'STATIC_ASSERT'
STATIC_ASSERT(1 > 2) 的扩展中;
^
Full test results here:
完整的测试结果在这里:
/*
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;
}
Related:
有关的:
回答by melpomene
From Perl, specifically perl.hline 3455(<assert.h>is included beforehand):
从 Perl,特别是perl.h第 3455 行(<assert.h>预先包括在内):
/* STATIC_ASSERT_DECL/STATIC_ASSERT_STMT are like assert(), but for compile
time invariants. That is, their argument must be a constant expression that
can be verified by the compiler. This expression can contain anything that's
known to the compiler, e.g. #define constants, enums, or sizeof (...). If
the expression evaluates to 0, compilation fails.
Because they generate no runtime code (i.e. their use is "free"), they're
always active, even under non-DEBUGGING builds.
STATIC_ASSERT_DECL expands to a declaration and is suitable for use at
file scope (outside of any function).
STATIC_ASSERT_STMT expands to a statement and is suitable for use inside a
function.
*/
#if (defined(static_assert) || (defined(__cplusplus) && __cplusplus >= 201103L)) && (!defined(__IBMC__) || __IBMC__ >= 1210)
/* static_assert is a macro defined in <assert.h> in C11 or a compiler
builtin in C++11. But IBM XL C V11 does not support _Static_assert, no
matter what <assert.h> says.
*/
# define STATIC_ASSERT_DECL(COND) static_assert(COND, #COND)
#else
/* We use a bit-field instead of an array because gcc accepts
'typedef char x[n]' where n is not a compile-time constant.
We want to enforce constantness.
*/
# define STATIC_ASSERT_2(COND, SUFFIX) \
typedef struct { \
unsigned int _static_assertion_failed_##SUFFIX : (COND) ? 1 : -1; \
} _static_assertion_failed_##SUFFIX PERL_UNUSED_DECL
# define STATIC_ASSERT_1(COND, SUFFIX) STATIC_ASSERT_2(COND, SUFFIX)
# define STATIC_ASSERT_DECL(COND) STATIC_ASSERT_1(COND, __LINE__)
#endif
/* We need this wrapper even in C11 because 'case X: static_assert(...);' is an
error (static_assert is a declaration, and only statements can have labels).
*/
#define STATIC_ASSERT_STMT(COND) STMT_START { STATIC_ASSERT_DECL(COND); } STMT_END
If static_assertis available (from <assert.h>), it is used. Otherwise, if the condition is false, a bit-field with a negative size is declared, which causes compilation to fail.
如果static_assert可用(来自<assert.h>),则使用它。否则,如果条件为假,则声明大小为负的位域,这会导致编译失败。
STMT_START/ STMT_ENDare macros expanding to do/ while (0), respectively.
STMT_START/STMT_END分别是扩展到do/ 的宏while (0)。
回答by Paolo.Bolzoni
The classic way is using an array:
经典的方法是使用数组:
char int_is_4_bytes_assertion[sizeof(int) == 4 ? 1 : -1];
It works because if the assertion is true the array has size 1 and it is valid, but if it is false the size of -1 gives a compilation error.
之所以有效,是因为如果断言为真,则数组的大小为 1 且有效,但如果为假,则大小为 -1 会产生编译错误。
Most compilers will show the name of the variable and point to the right part of the code where you can leave eventual comments about the assertion.
大多数编译器会显示变量的名称并指向代码的右侧部分,您可以在其中留下有关断言的最终注释。
回答by user4978854
This works, with "remove unused" option set. I may use one global function to check global parameters.
这有效,设置了“删除未使用”选项。我可能会使用一个全局函数来检查全局参数。
//
#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
//

