数据结构论坛

首页 » 分类 » 问答 » LinuxC语言结构体
TUhjnbcbe - 2020/12/7 13:40:00

前面学习了c语言的基本语法特性,本节进行更深入的学习。

预处理程序。编译指令:预处理,宏定义,

建立自己的数据类型:结构体,联合体,动态数据结构

c语言表达式工具逻辑运算符:

函数的递归调用方法

什么是预处理

vimhelloworld.c1

helloworld.c:

#includeintmain(){printf("hello,world!\n");return0;}

编译的目的:

从c语言.c源文件变成可执行文件

gcchelloworld.c-ohelloworld.out./helloworld.out12

编译的四个步骤:

.c文件-.i文件-.s文件-.o文件-可执行文件(可运行)

下面我们来查看预处理中要做的事情:

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语言常量分为直接常量和符号常量:

#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的大小,为了便于修改定义成宏。

通常定义数组我们这样写:

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和预处理是没

预览时标签不可点收录于话题#个上一篇下一篇
1
查看完整版本: LinuxC语言结构体