gcc预编译#if,#elif,#endif以及常用宏定义


转载:http://blog.chinaunix.net/uid-26854432-id-3181585.html
#if语句


#if语句会计算它后面的表达式,并检查表达式的结果,如果结果为真(true),就编译后面的语句,如果为假(false),就不编译后面的语句。

例如:

#if COUNT
char *desc = "The count is non-zero";
#endif


只有COUNT不为0的情况下才会编译

char *desc = "The count is non-zero";

语句。

下面是#if后面的表达式的一些语法规则:

1)表达式可以包括整形常量和宏,这个宏必须是被赋值过(区别被定义过)

2)可以使用圆括号来指定表达式的顺序。

3)表达式中可以包含C语言中的+,-,*,/,<<和>>算术运行符。这些算术运算使用当前平台的整型的最大表示的值来运算,一般为64位的整型值。

4)表达式也可以包括<,>,<=,>=和==比较运行符。

5)表达式可以包括&&和||逻辑运算符。

6)非(!)运算符可以反转表达式的结果,如:#if !(LIMXP > 12)

如果LIMXP > 12,那么表达式的结果就为假

7)#if也可以和defined语句一起使用,这样可以检查某一个宏是否被定义过(也可以直接使用#ifdef)。如:

#if defined(MINXP)

意思是如果MINXP被定义过,那么表达式就为真。也可以在前面加一个非(!)运算符来取反的结果,如:

#if !defined(MINXP)

意思是如果MINXP没有被定义过,那么表达式就为真。

8)一个标识符如果没有被定义过,那它的值总是为0,-Wundef选项可以用来产生这个警告信息。

9)有参数的宏(gcc:预处理语句--#define、#error和#warning )在表达式中也是零值,-Wundef选项可以用来产生这个警告信息。

10)#else提供另外的选择,如:

#if MINTXT <= 5
#define MINTLOG 11
#else
#define MINTLOG 14
#endif
11)#elif可以提供一个或多个的选择表达式,如:

#if MINTXT <= 5
#define MINTLOG 11
#elif MINTXT == 6
#define MINTLOG 12
#elif MINTXT == 7
#define MINTLOG 13
#else
#define MINTLOG 14
#endif



#ifdef

在跟#ifdef后面同一行的宏如果被定义过的话,那么#ifdef后面的语句块将会被编译,这个语句块以#endif结束。例如:

#ifdef MINTARRAY
int xarray[20];
#endif /* MINTARRAY */



#endif后面的注释语句并不是必须的/* MINTARRAY */,但确实一个好的管用法,这样就可以知道是你定义的哪个宏的结束位置。

和#ifdef意思想反的就是#ifndef了。
#if语句

#if语句会计算它后面的表达式,并检查表达式的结果,如果结果为真(true),就编译后面的语句,如果为假(false),就不编译后面的语句。

例如:

      #if COUNT
      char *desc = "The count is non-zero";
      #endif


只有COUNT不为0的情况下才会编译

char *desc = "The count is non-zero";

语句。

下面是#if后面的表达式的一些语法规则:

1)表达式可以包括整形常量和宏,这个宏必须是被赋值过(区别被定义过)

2)可以使用圆括号来指定表达式的顺序。

3)表达式中可以包含C语言中的+,-,*,/,<<和>>算术运行符。这些算术运算使用当前平台的整型的最大表示的值来运算,一般为64位的整型值。

4)表达式也可以包括<,>,<=,>=和==比较运行符。

5)表达式可以包括&&和||逻辑运算符。

6)非(!)运算符可以反转表达式的结果,如:#if !(LIMXP > 12)

      如果LIMXP > 12,那么表达式的结果就为假

7)#if也可以和defined语句一起使用,这样可以检查某一个宏是否被定义过(也可以直接使用#ifdef)。如:

     #if defined(MINXP)

     意思是如果MINXP被定义过,那么表达式就为真。也可以在前面加一个非(!)运算符来取反的结果,如:

     #if !defined(MINXP)

     意思是如果MINXP没有被定义过,那么表达式就为真。#if语句


#if语句会计算它后面的表达式,并检查表达式的结果,如果结果为真(true),就编译后面的语句,如果为假(false),就不编译后面的语句。

例如:

#if COUNT
char *desc = "The count is non-zero";
#endif


只有COUNT不为0的情况下才会编译

char *desc = "The count is non-zero";

语句。

下面是#if后面的表达式的一些语法规则:

1)表达式可以包括整形常量和宏,这个宏必须是被赋值过(区别被定义过)

2)可以使用圆括号来指定表达式的顺序。

3)表达式中可以包含C语言中的+,-,*,/,<<和>>算术运行符。这些算术运算使用当前平台的整型的最大表示的值来运算,一般为64位的整型值。

4)表达式也可以包括<,>,<=,>=和==比较运行符。

5)表达式可以包括&&和||逻辑运算符。

6)非(!)运算符可以反转表达式的结果,如:#if !(LIMXP > 12)

如果LIMXP > 12,那么表达式的结果就为假

7)#if也可以和defined语句一起使用,这样可以检查某一个宏是否被定义过(也可以直接使用#ifdef)。如:

#if defined(MINXP)

意思是如果MINXP被定义过,那么表达式就为真。也可以在前面加一个非(!)运算符来取反的结果,如:

#if !defined(MINXP)

意思是如果MINXP没有被定义过,那么表达式就为真。

8)一个标识符如果没有被定义过,那它的值总是为0,-Wundef选项可以用来产生这个警告信息。

9)有参数的宏(gcc:预处理语句--#define、#error和#warning )在表达式中也是零值,-Wundef选项可以用来产生这个警告信息。

10)#else提供另外的选择,如:

#if MINTXT <= 5
#define MINTLOG 11
#else
#define MINTLOG 14
#endif
11)#elif可以提供一个或多个的选择表达式,如:

#if MINTXT <= 5
#define MINTLOG 11
#elif MINTXT == 6
#define MINTLOG 12
#elif MINTXT == 7
#define MINTLOG 13
#else
#define MINTLOG 14
#endif



#ifdef

在跟#ifdef后面同一行的宏如果被定义过的话,那么#ifdef后面的语句块将会被编译,这个语句块以#endif结束。例如:

#ifdef MINTARRAY
int xarray[20];
#endif /* MINTARRAY */



#endif后面的注释语句并不是必须的/* MINTARRAY */,但确实一个好的管用法,这样就可以知道是你定义的哪个宏的结束位置。

和#ifdef意思想反的就是#ifndef了。

8)一个标识符如果没有被定义过,那它的值总是为0,-Wundef选项可以用来产生这个警告信息。

9)有参数的宏(gcc:预处理语句--#define、#error和#warning )在表达式中也是零值,-Wundef选项可以用来产生这个警告信息。

10)#else提供另外的选择,如:

#if MINTXT <= 5
#define MINTLOG 11
#else
#define MINTLOG 14
#endif
11)#elif可以提供一个或多个的选择表达式,如:

#if MINTXT <= 5
#define MINTLOG 11
#elif MINTXT == 6
#define MINTLOG 12
#elif MINTXT == 7
#define MINTLOG 13
#else
#define MINTLOG 14
#endif

 

#ifdef

在跟#ifdef后面同一行的宏如果被定义过的话,那么#ifdef后面的语句块将会被编译,这个语句块以#endif结束。例如:

#ifdef MINTARRAY
int xarray[20];
#endif /* MINTARRAY */

 

#endif后面的注释语句并不是必须的/* MINTARRAY */,但确实一个好的管用法,这样就可以知道是你定义的哪个宏的结束位置。

和#ifdef意思想反的就是#ifndef了。

其实这三个都是宏,下面详细的说明:

    你所遇到的这几个宏是为了进行条件编译。
    一般情况下,源程序中所有的行都参加编译。但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是“条件编译”。有时,希望当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。
    条件编译命令最常见的形式为:
    
#ifdef 标识符
    
程序段1
    
#else
    
程序段2
    
#endif
    

    它的作用是:当标识符已经被定义过(一般是用#define命令定义),则对程序段1进行编译,否则编译程序段2。
    其中#else部分也可以没有,即:
    
#ifdef
    
程序段1
    
#denif
    

    这里的“程序段”可以是语句组,也可以是命令行。这种条件编译可以提高C源程序的通用性。如果一个C源程序在不同计算机系统上系统上运行,而不同的计算机又有一定的差异。例如,我们有一个数据类型,在Windows平台中,应该使用long类型表示,而在其他平台应该使用float表示,这样往往需要对源程序作必要的修改,这就降低了程序的通用性。可以用以下的条件编译:
    
#ifdef WINDOWS
    
#define MYTYPE long
    
#else
    
#define MYTYPE float
    
#endif
    

    如果在Windows上编译程序,则可以在程序的开始加上
    
#define WINDOWS
    

    这样则编译下面的命令行:
    
#define MYTYPE long
    

    如果在这组条件编译命令之前曾出现以下命令行:
    
#define WINDOWS 0
    

    则预编译后程序中的MYTYPE都用float代替。这样,源程序可以不必作任何修改就可以用于不同类型的计算机系统。当然以上介绍的只是一种简单的情况,可以根据此思路设计出其它的条件编译。
    例如,在调试程序时,常常希望输出一些所需的信息,而在调试完成后不再输出这些信息。可以在源程序中插入以下的条件编译段:
    
#ifdef DEBUG
    
print ("device_open(%p)\n", file);
    
#endif
    

    如果在它的前面有以下命令行:
    
#define DEBUG
    

    则在程序运行时输出file指针的值,以便调试分析。调试完成后只需将这个define命令行删除即可。有人可能觉得不用条件编译也可达此目的,即在调试时加一批printf语句,调试后一一将printf语句删除去。的确,这是可以的。但是,当调试时加的printf语句比较多时,修改的工作量是很大的。用条件编译,则不必一一删改printf语句,只需删除前面的一条“#define DEBUG”命令即可,这时所有的用DEBUG作标识符的条件编译段都使其中的printf语句不起作用,即起统一控制的作用,如同一个“开关”一样。
    有时也采用下面的形式:
    
#ifndef 标识符
    
程序段1
    
#else
    
程序段2
    
#endif
    

    只是第一行与第一种形式不同:将“ifdef”改为“ifndef”。它的作用是:若标识符未被定义则编译程序段1,否则编译程序段2。这种形式与第一种形式的作用相反。
    以上两种形式用法差不多,根据需要任选一种,视方便而定。
    还有一种形式,就是#if后面的是一个表达式,而不是一个简单的标识符:
    
#if 表达式
    
程序段1
    
#else
    
程序段2
    
#endif
    

    它的作用是:当指定的表达式值为真(非零)时就编译程序段1,否则编译程序段2。可以事先给定一定条件,使程序在不同的条件下执行不同的功能。
    例如:输入一行字母字符,根据需要设置条件编译,使之能将字母全改为大写输出,或全改为小写字母输出。
    
#define LETTER 1
    
main()
    
{
    
char str[20]="C Language",c;
    
int i=0;
    
while((c=str[i])!='\0'){
    
i++;
    
#if LETTER
    
if(c>='a'&&c<='z') c=c-32;
    
#else
    
if(c>='A'&&c<='Z') c=c+32;
    
#endif
    
printf("%c",c);
    
}
    
}
    

    运行结果为:C LANGUAGE
    现在先定义LETTER为1,这样在预处理条件编译命令时,由于LETTER为真(非零),则对第一个if语句进行编译,运行时使小写字母变大写。如果将程序第一行改为:
    
#define LETTER 0
    

    则在预处理时,对第二个if语句进行编译处理,使大写字母变成小写字母(大写字母与相应的小写字母的ASCII代码差32)。此时运行情况为:
    c language
    有人会问:不用条件编译命令而直接用if语句也能达到要求,用条件编译命令有什么好处呢?的确,此问题完全可以不用条件编译处理,但那样做目标程序长(因为所有语句都编译),而采用条件编译,可以减少被编译的语句,从而减少目标的长度。当条件编译段比较多时,目标程序长度可以大大减少。
 
头文件中的#ifndef


千万不要忽略了头件的中的#ifndef,这是一个很关键的东西。比如你有两个C文件,这两
个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件
,于是问题来了,大量的声明冲突。

还是把头文件的内容都放在#ifndef和#endif中吧。不管你的头文件会不会被多个文件引用
管你的头文件会不会被多个文件引用
,你都要加上这个。一般格式是这样的:

#ifndef <标识>
#define <标识>

......
......

#endif

<标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。
标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划
线,如:stdio.h

#ifndef _STDIO_H_
#define _STDIO_H_

......

#endif

 C语言常用宏定义技巧

用C语言编程,宏定义是个很重要的编程技巧。用好了宏定义,它可以增强程序的可读性、可移植性、方便性、灵活性等等。

1.         防止一个头文件被重复包含:

#ifndef COMDEF_H

#define COMDEF_H

//头文件内容 

#endif

当你所建的工程有多个源文件组成时,很可能会在多个文件里头包含了同一个头文件,如果借用上面的宏定义就能够避免同一个头文件被重复包含时进行多次编译。因为当它编译第一个头文件时总是没有定义#define COMDEF_H,那么它将编译一遍头文件中所有的内容,包括定义#define COMDEF_H。这样编译再往下进行时如果遇到同样要编译的头文件,那么由于语句#ifndef COMDEF_H的存在它将不再重复的编译这个头文件。

2.         通过宏定义使得同一数据类型的字节数不因不同的平台或不同的编译器而改变:

typedef  unsigned  char      boolean;               /* Boolean value type */

 

       typedef  unsigned  long  int  uint32;                /* Unsigned 32bit value*/

       typedef  unsigned  short      uint16;                /* Unsigned 16bit value */

       typedef  unsigned  char      uint8;                 /* Unsigned 8bit value */

      

       typedef  signed  long  int    int32;                 /* Signed 32bit value */

       typedef  signed  short        int16;                 /* Signed 16bit value */

       typedef  signed  char        int8;                   /* Signed 8bit value */

3.         得到指定地址的一个字节或字:

#define  MEM_B(x)  (*((uint8 *)(x)))

#define  MEM_W(x)  (*((uint16 *)(x)))

注:类似于这种有多个字符串组成的宏定义一定要注意加上“()”,因为我们知道宏定义只是一种简单的字符替换功能。

4.         求最大值和最小值:

#define  MAX(x,y)  (((x)>(y))?(x):(y))

       #define  MAX(x,y)  (((x)<(y))?(x):(y))

5.         得到一个结构体中field所占用的字节数:

#define  FSIZ(type,field)  sizeof(((type *)0)->field)

6.         按照LSB格式把两个字节转化为一个Word:

#define  FLIPW(ray)   ((((word)(ray)[0])*256)+(ray)[1])

或可以是

#define  FLIPW(data1,data2)   ((((word)(data1))*256)+(data2))

7.         按照LSB格式把一个Word转化为两个字节:

#define  FLOPW(ray,val)  do{ (ray)[0]=((val)/256); (ray)[1] =((val)&0xFF);}while(0)

或可以是

#define  FLOPW(data1,data2,val)  do{ data1=((val)/256); data2 =((val)&0xFF);}while(0)

注:这里的do{}while(0)是为了防止多语句的宏定义在使用中出错。比如:

if(…)

 

       FLOPW(ray,val)

 

else

 

上面的程序代码中要是宏定义FLOPW(ray,val)中没有do{}while(0),这时我们应该将语句改为:

if(…)

{

       FLOPW(ray,val)

}

else

{

 

}

8.         得到一个变量的地址:

#define  B_PTR(var)  ((byte *)(void *)&(var))

#define  W_PTR(var)  ((word *)(void *)&(var))

注:宏定义语句中void *申明了该地址可以是任何类型变量的地址,byte *申明为单字节变量的地址,word *申明为双字节变量的地址。

9.         得到一个高位和低位字节:

#define  WORD_LO(xxx)  ((byte)((word)(xxx)&0xFF))

#define  WORD_HI(xxx)  ((byte)((word)(xxx)>>8))

10.     返回一个比X大的最接近的8的倍数:

#define  RND8(x)  ((((x)+7)/8)*8)

11.     将一个字母转换为大写:

#define  UPCASE(c)  ( ((c) >= ‘a’ && (c) <= ‘z’) ? ((c) – 0x20) : (c) )

相对应的还有将一个字母转换为小写:

#define  UPCASE(c)  ( ((c) >= ‘A’ && (c) <= ‘Z’) ? ((c) + 0x20) : (c) )

注:如果你记不住大写和小写之间的ASCII差值,可以将0x20换成(’a’ - ‘A’),但这里小写的ASCII值大于大写的ASCII值你该要记住的吧。

12.     防止溢出的一个方法:

#define  INC_SAT(val)  (val = ((val)+1 > (val)) ? (val)+1 : (val))

还可以做成循环计数的(加入计数器val是个无符号整数):

#define  INC_SAT(val)  (val = ((val)+1 > (val)) ? (val)+1 : (0))

13.     宏中“#”和“##”的用法:

一般用法:

使用“#”把宏参数变为一个字符串,用”##”把两个宏参数结合在一起

例程:

#include

#include

 

#define  STR(s)  #s

#define  CONS(a,b)  int(a##e##b)

 

int main()

{

       printf(STR(vck));         //输出字符串“vck”

       printf(“%d”,CONS(2,3));   //2e3输出:2000

       return 0;

}

 

当宏参数是另一个宏的时候,注意宏定义里有用“#”或“##”的地方宏参数是不会再展开的:

非“#”和“##”情况:

例程:

#define  TOW  (2)

#define  MUL(a,b)  (a*b)

 

printf(“%d*%d=%d”,TOW,TOW,MUL(TOW,TOW));

//该行可展开为printf(“%d*%d=%d”,(2),(2),((2)*(2)));

 

有“#”或“##”情况:

例程:

#define  A  (2)

#define  STR(s)  #s

#define  CONS(a,b)  int(a##e##b)

 

printf(“int max: %s”,STR(INT_MAX)); //INT_MAX 在include

//该行展开为printf(“int max: %s”,”INT_MAX”);

//即这里只是展开宏STR(s),而下一级宏INT_MAX没有被展开

 

printf(“%s”,CONS(A,A));

//这一行编译将会产生错误,因为它展开为printf(“%s”,int(AeA));

 

为了解决其不能展开的问题,我们可以多加一层中间转换宏以实现所有宏的展开,如果你在编程时不确定你是否用到宏的嵌套问题,最好都加一级中间转换宏,以免产生错误。

例程:

#define  A  (2)

#define  _STR(s)  #s

#define  STR(s)  _STR(s)

#define  _CONS(a,b)  int(a##e##b)

#define  CONS(a,b)  _CONS(a,b)

 

printf(“int max: %s”,STR(INT_MAX));

//INT_MAX将被展开,它是int型的最大值,输出为:int max: 0x7fffffff

printf(“%d”,CONS(A,A));

//两层宏都将能够展开,CONS(A,A)à _CONS((2), (2))àint((2)e(2)),输出为:200

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值