【转】C语⾔之详解#ifdef等宏及妙⽤
这⼏个宏是为了进⾏条件编译。⼀般情况下,源程序中所有的⾏都参加编译。但是有时希望对其中⼀部分内容只在满⾜⼀定条件才进⾏编译,也就是对⼀部分内容指定编译的条件,这就是“条件编译”。有时,希望当满⾜某条件时对⼀组语句进⾏编译,⽽当条件不满⾜时则编译另⼀组语句。
条件编译命令最常见的形式为:
#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语句也能达到要求,⽤条件编译命令有什么好处呢?的确,此问题完全可以不⽤条件编译处理,但那样做⽬标程序长(因为所有语句都编译),⽽采⽤条件编译,可以减少被编译的语句,从⽽减少⽬标的长度。当条件编译段⽐较多时,⽬标程序长度可以⼤⼤减少。
浅谈#ifdef在软件开发中的妙⽤
  笔者从事UNIX环境下某应⽤软件的开发与维护⼯作,⽤户分布于全国各地,各⽤户需要的基本功能都是⼀样的,但在某些功能上要随着需求变化,不断加以升级,要想实现全国各地⽤户的升级⼯作是很困难的,⽽我们则只是利⽤E-mail发送补丁程序给⽤户,这些补丁程序都是在⼀套软件的基础上不断地修改与扩充⽽编写的,并由不同的标志⽂件转⼊到不同的模块,虽然程序体积在不断扩⼤,但丝毫不影响⽼⽤户的功能,这主要是得益于C程序的#ifdef/#else/#endif的作⽤。
  我们主要使⽤以下⼏种⽅法,假设我们已在程序⾸部定义#ifdef DEBUG与#ifdef TEST:
  1.利⽤#ifdef/#endif将某程序功能模块包括进去,以向某⽤户提供该功能。
  在程序⾸部定义#ifdef HNLD:
  #ifdef HNLD
  include"n166_hn.c"
  #endif
define的基本用法
  如果不许向别的⽤户提供该功能,则在编译之前将⾸部的HNLD加⼀下划线即可。
  2.在每⼀个⼦程序前加上标记,以便追踪程序的运⾏。
  #ifdef DEBUG
  printf(" Now is in hunan !");
  #endif
  3.避开硬件的限制。有时⼀些具体应⽤环境的硬件不⼀样,但限于条件,本地缺乏这种设备,于是绕过硬件,直接写出预期结果。具体做法是:
  #ifndef TEST
  i=dial();
  //程序调试运⾏时绕过此语句
  #else
  i=0;
  #endif
  调试通过后,再屏蔽TEST的定义并重新编译,即可发给⽤户使⽤了。
# ifdef #ifndef 等⽤法(转)
  头件的中的#ifndef,这是⼀个很关键的东西。⽐如你有两个C⽂件,这两个C⽂件都include了同⼀个头⽂件。⽽编译时,这两个C⽂件要⼀同编译成⼀个可运⾏⽂件,于是问题来了,⼤量的声明冲突。
还是把头⽂件的内容都放在#ifndef和#endif中吧。不管你的头⽂件会不会被多个⽂件引⽤,你都要加上这个。⼀般格式是这样的:
#ifndef <;标识>
#define <;标识>
......
......
#endif
<;标识>在理论上来说可以是⾃由命名的,但每个头⽂件的这个“标识”都应该是唯⼀的。标识的命名规则⼀般是头⽂件名全⼤写,前后加下划线,并把⽂件名中的“.”也变成下划线,如:stdio.h
#ifndef _STDIO_H_
#define _STDIO_H_
......
#endif
2.在#ifndef中定义变量出现的问题(⼀般不定义在#ifndef中)。
#ifndef AAA
#define AAA
...
int i;
...
#endif
⾥⾯有⼀个变量定义
在vc中链接时就出现了i重复定义的错误,⽽在c中成功编译。
结论:
(1).当你第⼀个使⽤这个头的.cpp⽂件⽣成.obj的时候,int i 在⾥⾯定义了当另外⼀个使⽤这个的.cpp再次[单独]⽣成.obj的时候,int i ⼜被定义然后两个obj被另外⼀个.cpp也include 这个头的,连接在⼀起,就会出现重复定义.
(2).把源程序⽂件扩展名改成.c后,VC按照C语⾔的语法对源程序进⾏编译,⽽不是C++。在C语⾔中,
若是遇到多个int i,则⾃动认为其中⼀个是定义,其他的是声明。
(3).C语⾔和C++语⾔连接结果不同,可能(猜测)是在进⾏编译的时候,C++语⾔将全局
变量默认为强符号,所以连接出错。C语⾔则依照是否初始化进⾏强弱的判断的。(参考)
解决⽅法:
(1).把源程序⽂件扩展名改成.c。
(2).推荐解决⽅案:
.h中只声明 extern int i;在.cpp中定义
<x.h>
#ifndef __X_H__
#define __X_H__
extern int i;
#endif //__X_H__
<x.c>
int i;
注意问题:
(1).变量⼀般不要定义在.h⽂件中。
【转】

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。