C语言带常量参数宏的编译时参数检查,有办法实现吗

简介:

定义一个带参数的宏
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.*/
复制代码










本文转自 wws5201985 51CTO博客,原文链接:http://blog.51cto.com/wws5201985/772303,如需转载请自行联系原作者
目录
相关文章
|
3月前
|
存储 自然语言处理 编译器
【C语言】编译与链接:深入理解程序构建过程
【C语言】编译与链接:深入理解程序构建过程
|
1月前
|
存储 算法 C语言
【C语言】字符常量详解
字符常量是C语言中处理字符数据的重要工具。通过单引号括起一个字符,我们可以方便地使用字符常量进行字符判断、字符运算和字符串处理等操作。理解字符常量的表示方法、使用场景和ASCII码对应关系,对于编写高效的C语言程序至关重要。
183 11
|
1月前
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
50 7
|
1月前
|
C语言
【C语言】全局搜索变量却找不到定义?原来是因为宏!
使用条件编译和 `extern` 来管理全局变量的定义和声明是一种有效的技术,但应谨慎使用。在可能的情况下,应该优先考虑使用局部变量、函数参数和返回值、静态变量或者更高级的封装技术(如结构体和类)来减少全局变量的使用。
41 5
|
1月前
|
C语言
【C语言】<常量> 之群英荟萃
在C语言中,常量(Constants)是指在程序运行过程中其值不能被修改的固定值。常量包括数值常量(整型和浮点型)、字符常量、字符串常量、使用const关键字定义的常量变量以及枚举常量。
38 4
|
1月前
|
编译器 C语言
【C语言】常量的 “前缀和后缀” 大通关!
在C语言中,常量的前缀和后缀用于明确指定常量的类型和进制系统。前缀主要用于区分不同进制的数字常量,而后缀则用于区分不同类型的整数和浮点数。正确使用前缀和后缀,可以提高代码的可读性和可维护性,确保编译器正确地理解和处理常量。
49 1
|
1月前
|
存储 自然语言处理 Unix
【C语言】C语言 4 个编译过程详解
编译是将源代码转换为目标代码的过程。它是在编译器的帮助下完成的。编译器检查源代码是否存在语法或结构错误,如果源代码没有错误,则生成目标代码。
67 1
|
3月前
|
存储 自然语言处理 编译器
|
3月前
|
编译器 Linux C语言
【C语言篇】编译和链接以及预处理介绍(下篇)
【C语言篇】编译和链接以及预处理介绍(下篇)
39 1
【C语言篇】编译和链接以及预处理介绍(下篇)
|
3月前
|
自然语言处理 编译器 Linux
C语言中抽象的编译和链接原理
C语言中抽象的编译和链接原理
34 1