定义一个带参数的宏
01.#define MACRO_P(A) \
02.{ \
03. some.a = A; \
04. some.b = A+1; \
05. some.c[A] = 0; \
06.}
07./* 用法,参数约定为只能是整型常量,范围在0..50 */
08.MACRO_P(0);
09.MACRO_P(33);
复制代码其中参数A使用时必为常量,且在0..50范围内。那么如何在编译时判断越界并报错停止编译,而不是在运行时?
有可能实现吗?如果换成C++呢?
公开答案,其实就是用static_assert方法,这个方法是D语言最早官方支持,当然C和C++也都实现了,能保证程序的正确性并且没有运行时开销:
1. C_1x标准提供了_Static_assert()。gcc 4.6以后开始支持。
文件s.c
01./* C_1x standard _Static_assert version */
02.#include <stdio.h>
03.#define R 50
04.#define M(A) \
05.{ \
06._Static_assert( A>=0 && A<=R, "in M(A) A out of range" ); \
07. s.a = A; \
08. s.b = A+1; \
09. s.c[A] = 0; \
10.}
11.typedef struct {
12. int a;
13. int b;
14. int c[R+1];
15.} s_t;
16.s_t s;
17.int main(int argc, char ** argv)
18.{
19. int i=0;
20. M(51);
21. M(i);
22. return 0;
23.}
24.
25./* compile time error message:
26.$ gcc -o s s.c
27.s.c: In function ‘main’:
28.s.c:20:2: error: static assertion failed: "in M(A) A out of range"
29.s.c:21:2: error: expression in static assertion is not constant
30.$
31.*/
复制代码2.一个不完美的数组下标不能为负的方案,ANSI C实现。
文件:a.c
01./* ANSI C version */
02.#include <stdio.h>
03.#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]
04.#define R 50
05.#define M(A) \
06.{ \
07.STATIC_ASSERT( A>=0 && A<=R, in_M_A_out_of_range ); \
08. s.a = A; \
09. s.b = A+1; \
10. s.c[A] = 0; \
11.}
12.typedef struct {
13. int a;
14. int b;
15. int c[R+1];
16.} s_t;
17.s_t s;
18.int main(int argc, char ** argv)
19.{
20. int i=0;
21. M(51);
22. M(i);
23. return 0;
24.}
25.
26./* compile time error message:
27.$ gcc -o a a.c
28.a.c: In function ‘main’:
29.a.c:21:1: error: size of array ‘static_assertion_in_M_A_out_of_range’ is negative
30.$
31.不完美,变量i作参数没有报错,原因是C99支持
32.
33.Z:\>cl a.c
34.Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
35.Copyright (C) Microsoft Corporation. All rights reserved.
36.
37.a.c
38.a.c(21) : error C2118: negative subscript
39.a.c(22) : error C2057: expected constant expression
40.a.c(22) : error C2466: cannot allocate an array of constant size 0
41.
42.Z:\>
43.msvc由于不支持C99,反而能找到第二个问题。
44.*/
复制代码3. 利用gcc扩展的实现
文件:n.c
01./* GCC version */
02.#include <stdio.h>
03.#define CTC(X) ({ extern int __attribute__((error("assertion failure: '" #X "' not true"))) compile_time_check(); ((X)?0:compile_time_check()),0; })
04.#define R 50
05.#define M(A) \
06.{ \
07.CTC( A>=0 && A<=R ); \
08. s.a = A; \
09. s.b = A+1; \
10. s.c[A] = 0; \
11.}
12.typedef struct {
13. int a;
14. int b;
15. int c[R+1];
16.} s_t;
17.s_t s;
18.int main(int argc, char ** argv)
19.{
20. int i=0;
21. M(51);
22. M(i);
23. return 0;
24.}
25.
26./* compile time error message:
27.$ gcc -o n n.c
28.n.c: In function ‘main’:
29.n.c:21:2: error: call to ‘compile_time_check’ declared with attribute error: assertion failure: 'i>=0 && i<=R' not true
30.n.c:22:2: error: call to ‘compile_time_check’ declared with attribute error: assertion failure: 'i>=0 && i<=R' not true
31.$
32.*/
复制代码4.利用位域的C实现,但用了__COUNTER__
01./* ANSI C version 2 */
02.#include <stdio.h>
03.#define CTASTR2(pre,post) pre ## post
04.#define CTASTR(pre,post) CTASTR2(pre,post)
05.#define STATIC_ASSERT(cond,msg) \
06. typedef struct { int CTASTR(static_assertion_failed_,msg) : !!(cond); } \
07. CTASTR(static_assertion_failed_,__COUNTER__)
08.#define R 50
09.#define M(A) \
10.{ \
11.STATIC_ASSERT( A>=0 && A<=R, in_M_A_out_of_range ); \
12. s.a = A; \
13. s.b = A+1; \
14. s.c[A] = 0; \
15.}
16.typedef struct {
17. int a;
18. int b;
19. int c[R+1];
20.} s_t;
21.s_t s;
22.int main(int argc, char ** argv)
23.{
24. int i=0;
25. M(51);
26. M(i);
27. return 0;
28.}
29.
30./* compile time error message:
31.$ gcc -o b b.c
32.b.c: In function ‘main’:
33.b.c:25:2: error: zero width for bit-field ‘static_assertion_failed_in_M_A_out_of_range’
34.b.c:26:2: error: bit-field ‘static_assertion_failed_in_M_A_out_of_range’ width not an integer constant
35.$
36.
37.Z:\>cl b.c
38.Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
39.Copyright (C) Microsoft Corporation. All rights reserved.
40.
41.b.c
42.b.c(25) : error C2149: 'static_assertion_failed_in_M_A_out_of_range' : named bit
43.field cannot have zero width
44.b.c(26) : error C2057: expected constant expression
45.b.c(26) : error C2149: 'static_assertion_failed_in_M_A_out_of_range' : named bit
46.field cannot have zero width
47.
48.Z:\>
49.*/
复制代码