C语言之先处理

  这是2016年的最后一篇博客,年初自然的计划是形容12首博客,每月同样篇,1/3转载,2/3原创,看来是实现不了了!

题外话。今天如描绘的东西是C语言中之预处理器,我们经常说的宏定义的用法。为什么而描绘这东西吧,原因非常粗略:之前对先处理了解未充分。如果您针对C语言只是摸底或是止以高校中修了C语言,说到先处理量你独自掌握下面这长达告句子:(因为我虽是这种气象,哈哈!)

1 #define name value

  我又修预处理直接的驱动力是看了php的源码,开头一可怜推进的宏定义器,之前’掌握’的少数#define的用法太少了,根本看无晓源码中宏的拍卖逻辑和运转的路子。所以又上预处理器很有必要,里面好多事物其实并无麻烦,只是你莫碰到,等你读书了,就感觉到好了。

  一、宏定义和行使着之坑

  这小节采用先为代码再作证的款型,这样你得省每个代码的周转结果是否和您预期的相同!

  宏是啊,宏就是#define机制把指定的参数替换的文件中,这样的兑现方式就是巨。使用宏定义可以挤出频繁调用的函数,加快推行之快慢。定义如下:#define
name(参数)  执行体… 
“参数”可以是使逗号分隔的参数列表,这些参数可以给采取至执行体中,必须要小心的凡“参数”的左括如泣如诉要跟宏名字紧邻,不然编辑器会报错,或者让分解成执行体中之同一片。比如你写了一个
TEST(a) a * a 调用实践的时写及 TEST(1) 实际履行之凡替换后的 1 * 1。

  凡事都有利弊,宏定义固然使用方便,并且拥有函数不可比拟的履行进度,但是宏定义中有很多的坑,下面就说一样游说这个坑。看下面的代码:

 1 #include <stdio.h>
 2 
 3 #define TEST(a) a * a
 4 
 5 int main() {
 6     int b = TEST(2);
 7     int c = TEST(1+2);
 8     printf("b=%d, c=%d", b, c);
 9     printf("\n\n");
10 }

  没有实施之情景下,你感觉得到的结果是稍微为!好多总人口不加思索的说:b=4,c=9。如果算这样,就不有坑了,实际打印出来是:b=4,
c=5
,为什么c的值与预期的会发出偏差,其实你把执行体中的值替换一下跃跃欲试,就容易察觉题目了,当输入1+2之时段,宏替换成了
1+2*1+2,当然就是5了。好了知道了,那若学会了为?学会了再拘留一个:

 1 #include <stdio.h>
 2 
 3 #define TEST(a,b) ((a) > (b) ? (a) : (b))
 4 
 5 int main() {
 6     int zyf = 1;
 7     int abc = 2;
 8     int ret = TEST(zyf++, abc++);
 9     printf("zyf=%d,abc=%d,ret=%d", zyf, abc, ret);
10     printf("\n\n");
11 }

  输出多少吧,如果是 zyf=2,abc=3,ret=3
就蹭了,实际结果是:zyf=2,abc=4,ret=3
。道理和前面的如出一辙,只拘留替换后底结果才能真的看到答案。

  这样的题目防不胜防,怎样才能解决也,其实办法很粗略,错误的原故是实施的依次与咱们预料的莫雷同,那补加小括声泪俱下当可以解决这种问题。
比如 (a) * (a)。这样其实呢无是最为万咸的不二法门,比如您看之:ADD(a)  (a) +
(a) ,如果这样调用:ADD(2) * 5 ,这样以格外了,被调换成了 (a) + (a) *
5 执行顺序及预期的尚是休均等,所以还要当极度外层加上括号:((a) +
(a)),这样即便迎刃而解了。

 

  二**、预定义符号**

  C语言中来几乎单预定义的标志,还是有必不可少跟豪门说达等同说,先看一样段落代码:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #define VAR_DUMP printf( \
 4     "[\n \tfile:%s\n" \
 5     "\tline:%d\n" \
 6     "\ttime:%s %s\n" \
 7     "\tvalue:%d\n]", \
 8     __FILE__, __LINE__, __DATE__, __TIME__, value \
 9 )
10 int main() {
11     int value = 1;
12     VAR_DUMP;
13     printf("\n\n");
14 }

  是不是和你在大学念书之起接触未一样,最简便易行的宏定义可以利用#define
name value
的主意,当然也可将价值写成一个函数,运行的时一直调换函数。这个宏定义是包裹了调剂方法,是打印变量内容会像PHP中var_dump()或者print_r()函数一样,打印出变量的始末。

自从马上段代码中能够念及几接触内容:

1、使用#define可以使任何公文替换到程序中,在主程序中君得肆意行使VAR_DUMP。

2、宏定义不因为分行结束,如果坏长之宏定义,你可在末加上反斜杠来分行,保持代码易读性。

3、你可定义频繁调用的函数为宏定义,这样好加速推行的速速,具体原因后会说到。

4、C语言有几乎只约定的符需要我们了解,很多时节特别有效:

  __FILE__ 预编译的文件名

     __LINE__
文件时推行之行号(执行到当下一行)

  __DATE__ 文件编译的日子

  __TIME__ 文件编译的具体时刻

     __STDC__ 是否遵循ANSI C
(不常用)

末段附上运行结果,如图:

C语言 1

  三**、宏替换的进程**

  在程序的编译阶段,宏先被实施替换,一般只要提到下面的步骤:

  1、调用宏的地方看是不是 进行了 #define定义,如果是就是进行轮换。

  2、把替换的文本信息插入到替换的位置,其中参数为替换成了实在的值。

  3、#define可以蕴涵其他概念的#define定义之物,需要注意的凡匪能够出现递归的情况。

  因为替换存在即字段自动结合,所以可以使用部分精彩绝伦的方案:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 #define VAR_DUMP(A,B)\
 5    printf("Value of " #B " is " A "\n", B)
 6 
 7 int main(){
 8     int x = 1;
 9     VAR_DUMP("%d", x+2);
10 }

  

  四**、条件编译和任何宏用法**

  于巨型的C程序中公能见到许多的原则编译,比如可依据当前之条件加载不同的宏配置,或者以编译的时段增长直极预设的编译条件。这些事物的落实都距不起条件编译。

  1、条件嵌套,#if   #endif 原型:

1 #if condition
2     执行体
3 #endif

得依据condition来规定执行体要无设实践,以之来支配在不同的条件下编译成不同的系统。看下的代码,当把DEBUG定义成非0值时,MAX宏定义是有的,当定义成0时,程序就算见面报错。

 1 #include <stdio.h>
 2 
 3 #define DEBUG 0
 4 #if DEBUG
 5     #define MAX(a) ((a) * (a))
 6 #endif
 7 
 8 int main() {
 9     int b = MAX(2);
10     int c = MAX(1+2);
11     printf("b=%d, c=%d", b, c);
12     printf("\n\n");
13 }

当然#if 也可以同#elif嵌套使用,这样即使和我们在函数里采取if
else一样了,下面是一致段落php源码中之等同段子话,你能够见到编译php指定不同的参数,检查不同之条件等等都好由此先行处理着之法编译开得。

 1 #ifndef PHP_H
 2     #define PHP_H
 3 
 4     #ifdef HAVE_DMALLOC
 5         #include <dmalloc.h>
 6     #endif
 7 
 8     #define PHP_API_VERSION 20100412
 9     #define PHP_HAVE_STREAMS
10     #define YYDEBUG 0
11 
12     #include "php_version.h"
13     #include "zend.h"
14     #include "zend_qsort.h"
15     #include "php_compat.h"
16     #include "zend_API.h"
17 
18     #undef sprintf
19     #define sprintf php_sprintf
20 
21     /* PHP's DEBUG value must match Zend's ZEND_DEBUG value */
22     #undef PHP_DEBUG
23     #define PHP_DEBUG ZEND_DEBUG
24 
25     #ifdef PHP_WIN32
26     #    include "tsrm_win32.h"
27     #    include "win95nt.h"
28     #    ifdef PHP_EXPORTS
29     #        define PHPAPI __declspec(dllexport)
30     #    else
31     #        define PHPAPI __declspec(dllimport)
32     #    endif
33     #    define PHP_DIR_SEPARATOR '\\'
34     #    define PHP_EOL "\r\n"
35     #else
36     #    if defined(__GNUC__) && __GNUC__ >= 4
37     #        define PHPAPI __attribute__ ((visibility("default")))
38     #    else
39     #        define PHPAPI
40     #    endif
41 
42     #    define THREAD_LS
43     #    define PHP_DIR_SEPARATOR '/'
44     #    define PHP_EOL "\n"
45     #endif
46 
47     #ifdef NETWARE
48         /* For php_get_uname() function */
49         #define PHP_UNAME  "NetWare"
50         #define PHP_OS      PHP_UNAME
51     #endif
52 
53     #if HAVE_ASSERT_H
54         #if PHP_DEBUG
55             #undef NDEBUG
56         #else
57             #ifndef NDEBUG
58                 #define NDEBUG
59             #endif
60         #endif
61         #include <assert.h>
62 
63     #else /* HAVE_ASSERT_H */
64         #define assert(expr) ((void) (0))
65     #endif /* HAVE_ASSERT_H */
66 
67     #define APACHE 0
68     #if HAVE_UNIX_H
69         #include <unix.h>
70     #endif
71 
72     #if HAVE_ALLOCA_H
73         #include <alloca.h>
74     #endif
75 
76     #if HAVE_BUILD_DEFS_H
77         #include <build-defs.h>
78     #endif
79     . . . 

  2、是否已经被定义

      被定义:#if   define() 或者是#ifdef 

  不叫定义:#if !define() 或者是#ifndef

  前者的描摹法虽并未接班人精炼,但是前者有再次多的施用状况,比如下面这种,可以拓展嵌套执行。

1 #if defined(DEBUG)
2      #ifdef DEBUGTWO
3            #define TEST(a) a * a
4      #endif
5 #endif

  3、移除一个宏定义,当不再利用一个宏定义后,可以下undef来管非欲之宏移除,原型:

1 #undef name

 

  五**、宏命名规则和跟函数区别**

  从前方的使用受到我们好观看,宏的使用规则和函数真是同样型一样,但是精神上要来分别的,在利用受到争区别宏和函数,涉及到代码规范及代码的可读性问题。标准的宏使用相应采取非常写字母,这样在程序中随意地方用宏都会理解就是一个宏定义。比如前面用到之
#define TEST(a) ((a) * (a))。

  宏以及函数区别有以下几点:

  1、执行进度达到,宏定义更快,函数因为用调用栈,存在调用,返回,保存现场的系出,所以比宏要缓慢。

  2、代码长度达到,宏在代码长度达实在是加强之,每一样处之以宏都会拿name替换成宏内容如果大度应用,会是代码显著加强,函数代码只出一致客,比较节省代码空间。

  3、参数类型上,宏没有参数类型,只要可以
使用还施行。函数不相同,函数有参数类型确定性。正式为如此,有些宏能巧妙的运这或多或少,完成函数不克好的天职,看下代码(书及看之),巧妙的使传递类型无限制的风味自动开辟想只要之各种类型空间:

1 #include <stdio.h>
2 #include <stdlib.h>
3 
4 #define CREATE_P(nums, type) ((type *) malloc((nums) * sizeof(type)))
5 
6 int main(){
7     int nums = 2;
8     CREATE_P(nums, int);
9 }

  4、宏定义和函数的应用状况,宏定义一般以先后的发端,函数转化成宏定义一定要是考虑资金问题,短小精炼的函数转化成宏使用时最好好之,功能负责之函数转化成宏就生硌得不偿失了。

 

  六**、文件包含**

  1、本地文件包含和货栈文件包含  

  文件包含在巨型系统面临一定会为此到,大型系统宏定义巨多尽,不可能拿有的宏定义都复制到每个文件中,那么文件包含就能化解这种题材。

  实际上编辑器支持少数种文件包含,一栽是咱们常会面为此的库文件的盈盈,比如上面我们看看底:#include
<stdio.h>,还有雷同种植是当地文件包含,说白了不畏是咱们好写的文件,包含的原型如下:

1 #include <filename>
2 #include "filename"

  这片种植艺术还可以展开文件之含有,不同之是第一栽是堆栈文件的隐含,标准的C库函数都见面以.h扩展名结尾,第二种是本地文件包含,当编辑器看到第二栽方式时,优先找本路径下得地方库文件,如果没有找到就会如包含库文件那样以指定的途径下找,这时第二种与率先栽不畏多了。第二种植含方式在编码习惯及呢是于好的,别人看您的代码很易懂之文件是库函数还是你自己写的。

  1、嵌套文件包含

  大型系统被不仅仅产生雅量底文本包含,还见面产生恢宏的嵌套文件包含,看下的例子:

  a.h,b.h,c.h,define.c文件,其中a,b,c,define文件之始末如下:

 1 a.h:
 2 #include "c.h"
 3 void var_dumpa(){
 4     test obja;
 5     obja.a[1] = 2;
 6     printf("obja.a[1]: %d\n", obja.a[1]);
 7 }
 8 
 9 b.h:
10 #include "c.h"
11 void var_dumpb(){
12     test objb;
13     objb.a[1] = 2;
14     printf("objb.a[1]: %d\n", objb.a[1]);
15 }
16 
17 c.h:
18 #include <stdlib.h>
19 #include <stdio.h>
20 
21 typedef struct test{
22     int a[10];
23 }test;
24 
25 define.c:
26 #include <stdio.h>
27 #include "a.h"
28 #include "b.h"
29 
30 int main() {
31     var_dumpa();
32     var_dumpb();
33     printf("\n\n");
34 }

  ab文件包含c文件,define.c文件文件引用a,b文件后会见掀起一个荒唐:typedef
struct
test类型错误,因为c.h文件给含有了零星次等,像这种情景于大型系统遭到会时不时遇上,或者说,你见面发觉又引用库文件为未会见报错,由此可见,库文件一定是运了解决办法。其实解决这种不当的方案便是使规范编译,当此文件引入到外一个文件中晚我们可设置一个宏定义,比如:

1 #include <stdlib.h>
2 #include <stdio.h>
3 
4 #ifndef PATH_C_H
5     #define PATH_C_H 1
6     typedef struct test{
7         int a[10];
8     }test;
9 #endif

坐每次编译编译器都见面读入整个头文件,如果管所有的文本还长是条件编译的话,那交叉引用文件发出的重新宏编译问题虽迎刃而解了,运行如下:

C语言 2

  好了,就描写这样多吧,重新梳理了针对性宏定义的认识跟骨干的运用。时间仓促,出错的地方要大婶们肯定指出,万分谢谢!

注意:

1、本博客同步更新到自身之私家网站:http://www.zhaoyafei.cn

2、本文属原创内容,为了珍惜他人劳动,转载请注明本文地址:

http://www.cnblogs.com/zyf-zhaoyafei/p/6237295.html