前面学习了c语言的基本语法特性,本节进行更深入的学习。
预处理程序。编译指令:预处理,宏定义,
建立自己的数据类型:结构体,联合体,动态数据结构
c语言表达式工具逻辑运算符:
函数的递归调用方法
什么是预处理
vimhelloworld.c1
helloworld.c:
#includeintmain(){printf("hello,world!\n");return0;}
编译的目的/p>
从c语言.c源文件变成可执行文件
gcchelloworld.c-ohelloworld.out./helloworld.out12
编译的四个步骤/p>
.c文件-.i文件-.s文件-.o文件-可执行文件(可运行)
下面我们来查看预处理中要做的事情/p>
gcc-ohelloworld.ihelloworld.c-E1
-E表示只让gcc执行预处理。
//查看helloworld.i文件cathelloworld.i12
vim跳到整个文档底部,命令:
可以看到代码的底端是我们的main函数
对比一下.i文件和.c文件的区别
首先:它们都是c的语法。其次.c文件main函数上面是#include
而.i文件中这行代码不见了,变成了上面这些东西。
所以预处理所做的第一件事情就是展开头文件
将#include中stdio.h展开,将未注释的内容直接写入.i文件。
在预处理步骤中,除了展开头文件,还要进行宏替换。
宏是什么
c语言常量分为直接常量和符号常量/p>
#define标识符常量值(注意:没有分号)1
helloMacro.c源代码:
#include#defineR10intmain(){inta=R;printf("a=%d\n");printf("hello,world!\n");return0;}8910
gcc-ohelloMacro.ihelloMacro.c-E1
预处理过之后的代码
#4"helloworld.c"intmain(){inta=10;printf("a=%d\n");printf("hello,world!\n");return0;}8
可以看到10是直接当做一个字符串来替换原本的宏定义R。
宏的本质是发生在预处理阶段单纯的字符串替换(宏替换),在预处理阶段,宏不考虑语法;
示例代码2:vimhelloMacro2.c
#include#defineR10#defineMintmain(M){printf("hello,world!\n");return0;}8
gcchelloMacro2.c-ohelloMacro2.out./helloMacro2.out12
预处理是没有问题的,可以成功的编译执行。宏不考虑C语言的语法。它很单纯,字符串替换。
宏用于大量反复使用的常量、数组buffer的大小,为了便于修改定义成宏。
通常定义数组我们这样写/p>
inta[10];intb[10];12
定义两个相同大小的数组,这里我们就可以改为下面代码。
#defineR10inta[R];intb[R];
一次修改,可以修改两份。
宏也是可以传递参数的,可以做一些函数可以做的事情
宏函数
vimhelloMacroFunction.c源代码:
#include#defineR10#defineMintmain(#defineN(n)n*10M){inta=R;intb=N(a);printf("b=%d\n",b);printf("a=%d\n",a);printf("hello,world!\n");return0;}89101
gcchelloMacroFunction.c-ohelloMacroFunction.out./helloMacroFunction.out12
这里的处理过程:首先将参数a替换到上面的宏中,上面就变成了N(a)a*10,之后再用a*10替换下面的N(a)
intb=N(a);//变成了intb=a*10;1
gcc-ohelloMacroFunction.ihelloMacroFunction.c-E1
预处理之后:
#8"hello.c"intmain(){inta=10;intb=a*10;printf("b=%d\n",b);printf("a=%d\n",a);printf("hello,world!\n");return0;}89
先不考虑宏实现,先来写一个正常的求和函数。
vimhelloAdd.c1
#include#defineR20#defineMintmain(#defineN(n)n*10intadd(inta,intb){returnab;}M){inta=R;printf("a=%d\n",a);printf("hello,world!\n");intb=N(a);printf("b=%d\n",b);intc=add(a,b);printf("c=%d\n",c);return0;}891011613
gcchelloAdd.c-ohelloAdd.out./helloAdd.out12
使用宏函数实现求和。
vimhelloAddMacro.c1
#include#defineR20#defineMintmain(#defineN(n)n*10#defineADD(a,b)abintadd(inta,intb){returnab;}M){inta=R;printf("a=%d\n",a);printf("hello,world!\n");intb=N(a);printf("b=%d\n",b);intc=add(a,b);printf("c=%d\n",c);intd=ADD(a,b);printf("d=%d\n",d);return0;}89101161
gcchelloAddMacro.c-ohelloAddMacro.out./helloAddMacro.out12
可以看到使用宏函数和普通函数的求和效果是一致的。结果与简单的字符串替换一致。
ADD(a,b)被替换成ab因此式子变成intd=ab;
gcc-ohelloAddMacro.ihelloAddMacro.c-EvimhelloAddMacro.i12
版本3,宏定义中优先级问题。
#include#defineR20#defineMintmain(#defineN(n)n*10#defineADD(a,b)abintadd(inta,intb){returnab;}M){inta=R;printf("a=%d\n",a);printf("hello,world!\n");intb=N(a);printf("b=%d\n",b);intc=add(a,b);printf("c=%d\n",c);intd=ADD(a,b);printf("d=%d\n",d);inte=ADD(a,b)*ADD(a,b);printf("e=%d\n",e);return0;}89101161272829
预测一下e的输出为:ab*abab先乘起来,a=20,b=,ab=,然后加上a,b:得到结果()
gcchelloAddMacroPrecedence.c-ohelloAddMacroPrecedence.out./helloAddMacroPrecedence.out12
运算是等我们编译完了,执行的时候才会运行的。预处理阶段不会进行运算操作。
宏定义时由于本质是字符串的替换
真正运算的时候,会按照运算符号的优先级来进行
解决方案:
#defineADD(a,b)(ab)1
gcchelloAddMacroPrecedence.c-ohelloAddMacroPrecedence2.out./helloAddMacroPrecedence2.out12
加个括号,保证优先级更高一点。
宏函数和正常函数的优势?
正常的add函数需要返回值类型,需要传递进来的参数有类型要求。
讲传入的a,b类型进行改变,如变为两个浮点型数,程序就会自动类型转换。
但是宏函数就没有这种要求可以不用考虑输入值的类型,这与普通的函数定义不同。
intc=add(10.5,20.4);printf("c=%d\n",c);floatd=ADD(10.5,20.4);printf("d=%f\n",d);45
gcchelloAddMacroPrecedenceCompare.c-ohelloAddMacroPrecedenceCompare.out./helloAddMacroPrecedenceCompare.out12
普通函数例如intadd(inta,intb)除了在开头要声明值的类型,还要设置返回值,因此在定义过程与调用过程相对复杂。若能用宏定义实现的情况应优先考虑宏定义.
宏是不考虑数据类型,不考虑c语言的语法的。只是简单的字符串的处理。
预处理阶段,除了宏之外,还提供了一个叫做mtianyan:条件编译的功能。
可以按照不同的条件,编译不同的程序部分,从而产生不同的目标代码文件。对于程序的移植和调试都是很有用的。
下集预告:和宏比较相近的功能,typedef
LinuxC预处理之typedef
严格来讲,typedef和预处理是没
预览时标签不可点收录于话题#个上一篇下一篇