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,如需转载请自行联系原作者
目录
相关文章
|
1月前
|
存储 自然语言处理 编译器
【C语言】编译与链接:深入理解程序构建过程
【C语言】编译与链接:深入理解程序构建过程
|
1月前
|
自然语言处理 编译器 Linux
【C语言篇】编译和链接以及预处理介绍(上篇)1
【C语言篇】编译和链接以及预处理介绍(上篇)
40 1
|
1月前
|
存储 自然语言处理 编译器
|
1月前
|
编译器 Linux C语言
【C语言篇】编译和链接以及预处理介绍(下篇)
【C语言篇】编译和链接以及预处理介绍(下篇)
32 1
【C语言篇】编译和链接以及预处理介绍(下篇)
|
1月前
|
自然语言处理 编译器 Linux
C语言中抽象的编译和链接原理
C语言中抽象的编译和链接原理
20 1
|
1月前
|
存储 C语言
初识C语言:常量与变量中寻找数据类型
初识C语言:常量与变量中寻找数据类型
|
2月前
|
存储 C语言
【C语言基础考研向】02 数据类型-常量-变量
本文介绍了编程中的基本概念,包括数据类型分类、常量与变量的定义及使用。首先概述了四大类数据类型:基本类型(整型、浮点、字符型)、构造类型(数组、结构体)、指针类型和空类型。接着阐述了常量与变量的区别及命名规则,并详细说明了整型、浮点型和字符型数据的特点与应用。最后总结了常见的易错点,如字符串与字符常量的区别及浮点数的默认输出格式。
|
2月前
|
安全 编译器 C语言
C语言常量的定义与使用的注意点
在 C 语言中,常量是在程序运行期间值不变的量,通过字面值、`#define` 或 `const` 关键字定义。字面常量直接在代码中表示固定值,如整数 `100`、浮点数 `3.14`、字符 `&#39;A&#39;` 和字符串 `&quot;Hello, World!&quot;`;`#define` 用于定义宏,如 `#define PI 3.14159`;`const` 则定义不可变变量,如 `const int daysInWeek = 7`。常量可用于数组大小、循环边界等场景,并能提升代码的可读性和可维护性。使用时需注意作用域、类型安全和命名,避免直接使用数字(魔法数字)。
|
1月前
|
存储 C语言
【C语言篇】编译和链接以及预处理介绍(上篇)2
【C语言篇】编译和链接以及预处理介绍(上篇)
36 0
|
2月前
|
C语言
C语言程序设计核心详解 第二章:数据与数据类型 4种常量详解 常见表达式详解
本文详细介绍了C语言中的数据与数据类型,包括常量、变量、表达式和函数等内容。常量分为整型、实型、字符型和字符串常量,其中整型常量有十进制、八进制和十六进制三种形式;实型常量包括小数和指数形式;字符型常量涵盖常规字符、转义字符及八进制、十六进制形式;字符串常量由双引号括起。变量遵循先定义后使用的规则,并需遵守命名规范。函数分为标准函数和自定义函数,如`sqrt()`和`abs()`。表达式涉及算术、赋值、自增自减和逗号运算符等,需注意运算符的优先级和结合性。文章还介绍了强制类型转换及隐式转换的概念。