本文将介绍C语言条件编译的详细情况,特别是关于#if,#ifdef,#ifndef,#endif,#else,#elif的相关信息。我们将通过案例分析、数据研究等多种方式,帮助您更全面地了解这个主题,
本文将介绍C语言条件编译的详细情况,特别是关于#if,#ifdef,#ifndef,#endif,#else,#elif的相关信息。我们将通过案例分析、数据研究等多种方式,帮助您更全面地了解这个主题,同时也将涉及一些关于# ifdef #ifndef 等用法(转)、#if 和 #ifdef 条件编译注意、#ifdef #if defined( ) #if (condition)的用法、#pragma once #ifndef,#define,#endif的知识。
本文目录一览:- C语言条件编译(#if,#ifdef,#ifndef,#endif,#else,#elif)(C语言条件编译语句)
- # ifdef #ifndef 等用法(转)
- #if 和 #ifdef 条件编译注意
- #ifdef #if defined( ) #if (condition)的用法
- #pragma once #ifndef,#define,#endif
C语言条件编译(#if,#ifdef,#ifndef,#endif,#else,#elif)(C语言条件编译语句)
条件编译(conditional compiling)命令指定预处理器依据特定的条件来判断保留或删除某段源代码。例如,可以使用条件编译让源代码适用于不同的目标系统,而不需要管理该源代码的各种不同版本。条件编译区域以 #if、#ifdef 或 #ifndef 等命令作为开头,以 #endif 命令结尾。条件编译区域可以有任意数量的 #elif 命令,但最多一个 #else 命令。以 #if 开头的条件编译区域具有下面的格式:
#if 表达式1 [ 组1] [#elif 表达式2 [ 组2]] ... [#elif 表达式n [ 组n ]] [#else [ 组n+1 ]] #endif
预处理器会依次计算条件表达式,直到发现结果非 0(也就是 true)的条件表达式。预处理器会保留对应组内的源代码,以供后续处理。如果找不到值为 true 的表达式,并且该条件式编译区域中包含 #else 命令,则保留 #else 命令组内的代码。
组 1、组 2 等代码段,可以包含任意 C 源代码,也可以包含更多的命令,包括嵌套的条件式编译命令。在预处理阶段结束时,没有被预处理器保留以用于后续处理的组会从程序中全部删除。
#if 和 #elif 命令
作为 #if 或 #elif 命令条件的表达式,必须是整数常量预处理器表达式。这与普通的整数常量表达式不同,主要区别在于:(1) 不能在 #if 或 #elif 表达式中使用类型转换运算符。
(2) 可以使用预处理运算符 defined。
(3) 在预处理器展开所有宏,并且计算完所有 defined 表达式之后,会使用字符 o 替换掉表达式中所有其他标识符或关键字。
(4) 表达式中所有带符号值都具有 intmax_t 类型,并且所有无符号值都具有 uintmax_t 类型。字符常量也会受该规则的影响。intmax_t 和 uintmax_t 定义在头文件 stdint.h 中。
(5) 预处理器会把字符常量和字符串字面量中的字符与转义序列转换成运行字符集中对应的字符。然而,字符常量在预处理器表达式和在后期编译阶段是否具有相同的值,取决于实现版本。
defined 运算符
一元运算符 defined 可以出现在 #if 或 #elif 命令的条件中。它的形式如下:defined 标识符 defined (标识符)
如果指定的 identifier 是一个宏名称(也就是说,它已被 #define 命令定义,并且未被 #undef 命令取消定义),则 defined 表达式会生成值 1。否则,defined 表达式会生成值 0。
defined 运算符相对于 #ifdef 和 #ifndef 命令的优点是:你可以在更大型的预处理器表达式中使用它的值。如下例所示:
#if defined( __unix__ ) && defined( __GNUC__ ) /* ... */ #endif
大多数编译器会提供预定义宏,例如上例所使用的宏,它用来识别目标系统和编译器。因此,在 Unix 系统中,通常预先定义好了宏 __unix__,而 GCC 编译器则会预先定义好了宏 __GNUC__。类似地,微软 Windows 平台上的 Visual C 编译器会自动定义好宏 _WIN32 和宏 _MSC_VER。
#ifdef 和 #ifndef 命令
你可以通过 #ifdef 和 #ifndef 命令测试某个宏是否已被定义。它们的语法是:#ifdef 标识符 #ifndef 标识符
这等同于下面的 #if 命令:
#if defined 标识符 #if !defined 标识符
如果 identifier 不是宏名称,则 #ifndef 标识符后面的条件代码被保留。
# 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文件中。
#if 和 #ifdef 条件编译注意
之前写程序很少用到这两个条件编译,只是在头文件的开头使用过 #ifdef ....<CODE>.... #endif,他是防止头文件被重复包含,导致的变量被多处声明或定义。
最近写程序发现 #if ...... #endif 也是特别好用的。
#if
他的后面是接一个表达式
#if (if_need_display)
//使用C语言库函数 sprintf() 把数字强制转换为字符串输出,这样真的可以减少很多工作量,比上面简单太多了。
sprintf(distance,"%.2f",real_distance);
show_string(5+72,10,distance);
#endif
这个后面我跟了一个定义的宏,if_need_display,当我需要使用中间那段代码的时候,只需把这个变量宏定义为1就可以了。
就是当表达式为真(非零值)时,那么编译器就会把 #if 与 #endif 之间的代码编译进去。它多用在需要调试的代码中。
因为有些代码非常占程序的体积,或者非常消耗单片机的资源 ,但是它在调试的时候又比较有用,调试完之后,就用不到了,比如串口,显示屏什么的。
这个时候, #if 就显得特别的贴心,温暖。
#ifdef
#ifdef 后面跟一个宏,这个宏可以没有值,只要在前面这个宏被定义了出来,就编译中间的代码。
#define if_need_display
#ifdef if_need_display
//使用C语言库函数 sprintf() 把数字强制转换为字符串输出,这样真的可以减少很多工作量,比上面简单太多了。
sprintf(distance,"%.2f",real_distance);
show_string(5+72,10,distance);
#endif
这里,我宏定义了if_need_flag ,所以会编译中间的代码,否则忽略。
#ifdef #if defined( ) #if (condition)的用法
一、引言
首先,让我们先从头文件开始,在很多头文件里,我们会看到这样的语句
#ifndef _MYHEADFILE_H
#define _MYHEADFILE_H
// .......语句......
#endif // _MYHEADFILE_H
为了避免同一个文件被include多次,我们常使用 #ifndef 进行判断,如果没有包含
_MYHEADFILE_H , 则使用#define 来定义一个宏 _MYHEADFILE_H , #endif 与#ifndef
首尾呼应,表示结束。
说到这里,我们有必要提一个C语言中的预处理器,预处理器是一个小软件,它可以在编译前处理C程序,它的行为是由预处理指令控制的。
二、常用预处理指令
1,宏定义 #define
2,文件包含 #include
3,条件编译 #if
#ifdef
#ifndef
#if defined
#if !defined
#elif
#else
#endif
#undef
-----------------
-
指令都是以#开始的,我们来看一下简单的宏定义(对象式宏)
#define 标准符 替换列表
#define PI 3.1415926
可以对类型重命名
#define BOOL int
宏可以带参数,也是常说的宏函数
#define 标识符(x1,x2...) 替换列表
特别注意的是标识符和(之间不能有空格,否则会被当成字符串替换。并且圆括号是必须的。#后面也不能加空格。
我们来看一下例子:
#define MAX(x,y) ((x)>(y)?(x):(y))
#define IS_EVEN(n) ((n)%2==0)
#define TOUPPER(c) (‘a’<(c)&&(c)<’z’?(c)-’a’+’A’(c))
#define SWAP(T,x,y) {T t=x; x=y; y=t}
还可以写得更复杂一点,比如我们来写一个宏函数,用它来验证一个日期是否合法
#define ISLEAP(y) ((y)%4==0&&(y)%100!=0||(y)%400==0)
#define ISSMALL(m) ((m)==4||(m)==6||(m)==9||(m)==11)
#define NORMAL(m) (ISSMALL(m)?30:31)
#define DAYS(y,m) ((m)==2?28+ISLEAP(y):NORMAL(m))
#define IN(x, from,to) ((x)>=(from)&&(x)<=(to))
#define VALID(y,m,d) ((y)>1600&&IN(m,1,12)&&IN(d,1,DAYS(y,m)))
- 下面我们来看看条件编译
#if (comdition)
{//语句##;}
#endif
如果(comdition)为真, 也就是逻辑1的话,编译下面的语句,如果(comdition)为假,即逻辑0,则不编译下面的语句。例子如下:
#define DEBUG 1 //后面必须有相应的值,否则会报错;要么就干脆不要定义,会被当作0,即条件不成立。
#if DEBUG //也可以写成 #if DEBUG>0
Printf(“Value of i:%d\n”, i);
Printf(“Value of j:%d\n”, j);
#endif
格式:
#if 常量表达式
常量表达式为0时,预处理器删除#if 和#endif中间的代码
#if 会把没有定义过的标准符视做为0, 如果没有定义DEBUG, 则测试#if DEBUG 会失败,但#if !DEBUG会成功。
可以用宏来定义文件名:
#if defined(IA32) //没有#if define这种写法
#define CPU_FILE “ia32.h”
#elif defined(IA64)
#define CPU_FILE “ia64.h”
#elif defined(AMD64)
#define CPU_FILE “amd64.h”
#endif
#include CPU_FILE
- 还可以取消已经定义的宏:
#undef VALUE // 解除语句定义
#define VALUE 1000 // 重新定义VALUE 为1000
#endif
如果检验没有定义,可以这样写:
#define VALUE 1000 // 定义VALUE 为1000
#endif
以上所用的宏中:
#undef为解除定义;
#ifndef是if not defined的缩写,也可以写成#if !defined 即如果没有定义;
#ifdef是if defined的缩写,也可以写成#if defined 即检查是否定义过;
#ifdef 和 #if defined 的区别,#ifndef 与#if !defined 的区别相类似,都在于后者可以组成复杂的预编译条件,前者只判断单个宏是否定义,例如:
#if defined(PERL_PACK_CAN_SHRIEKSIGN)
/* v */ SIZE16,
#else
0,
#endif
#ifdef PERL_PACK_CAN_SHRIEKSIGN
/* v */ SIZE16,
#else
0,
#endif
#ifdef是种简写,但不支持更复杂的表达式。
#ifdef HAVE_MYHEADER
#if VERSION > 3
...
# endif
#endif
这种情况用
#if defined(HAVE_MYHEADER) && VERSION > 3
...
#endif
更为合理
所谓的复合用法,注意defined里面判断的还是某个宏是否有定义,但是它可以与其他条件组合,而对于#ifdef语句就不存在这种用法。
#pragma once #ifndef,#define,#endif
#pragma once这是一个比较常用的C/C++杂注,只要在头文件的最开始加入这条杂注,就能够保证头文件只被编译一次。
#pragma once是编译器相关的,有的编译器支持,有的编译器不支持,具体情况请查看编译器API文档,不过现在大部分编译器都有这个杂注了。
#ifndef,#define,#endif是C/C++语言中的宏定义,通过宏定义避免文件多次编译。所以在所有支持C++语言的编译器上都是有效的,如果写的程序要跨平台,最好使用这种方式。
写法:
方式一:
#ifndef _SOMEFILE_H_
#define _SOMEFILE_H_
.......... // 一些声明语句
#endif
方式二:
#pragma once
... ... // 一些声明语句
比较:
#ifndef的方式依赖于宏名字不能冲突,这不光可以保证同一个文件不会被包含多次,也能保证内容完全相同的两个文件不会被不小心同时包含。当然,缺点就是如果不同头文件的宏名不小心“撞车”,可能就会导致头文件明明存在,编译器却硬说找不到声明的状况。
#pragma once则由编译器提供保证:同一个文件不会被编译多次。注意这里所说的“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。带来的好处是,你不必再费劲想个宏名了,当然也就不会出现宏名碰撞引发的奇怪问题。对应的缺点就是如果某个头文件有多份拷贝,本方法不能保证他们不被重复包含。当然,相比宏名碰撞引发的“找不到声明”的问题,重复包含更容易被发现并修正。
方式一由语言支持所以移植性好,方式二 可以避免名字冲突
#pragma once方式产生于#ifndef之后,因此很多人可能甚至没有听说过。目前看来#ifndef更受到推崇。因为#ifndef受语言天生的支持,不受编译器的任何限制;而#pragma once方式却不受一些较老版本的编译器支持,换言之,它的兼容性不够好。也许,再过几年等旧的编译器死绝了,这就不是什么问题了。
我还看到一种用法是把两者放在一起的:
#pragma once
#ifndef __SOMEFILE_H__
#define __SOMEFILE_H__
... ... // 一些声明语句
#endif
看起来似乎是想兼有两者的优点。不过只要使用了#ifndef就会有宏名冲突的危险,所以混用两种方法似乎不能带来更多的好处,倒是会让一些不熟悉的人感到困惑。
我们今天的关于C语言条件编译和#if,#ifdef,#ifndef,#endif,#else,#elif的分享已经告一段落,感谢您的关注,如果您想了解更多关于# ifdef #ifndef 等用法(转)、#if 和 #ifdef 条件编译注意、#ifdef #if defined( ) #if (condition)的用法、#pragma once #ifndef,#define,#endif的相关信息,请在本站查询。
本文标签: