第5章-宏-创新互联
目录
创新互联建站自成立以来,一直致力于为企业提供从网站策划、网站设计、做网站、成都做网站、电子商务、网站推广、网站优化到为企业提供个性化软件开发等基于互联网的全面整合营销服务。公司拥有丰富的网站建设和互联网应用系统开发管理经验、成熟的应用系统解决方案、优秀的网站开发工程师团队及专业的网站设计师团队。C语言编译过程
宏定义
带参宏和函数的区别
#define的特殊用法
选择性编译
宏的补充
C语言编译过程
预处理、编译、汇编、链接
1.预处理 gcc -E hello.c-ohello.i
2.编译 gcc -S hello.i -o hello.s
3.汇编 gcc -C hello.s-ohello.o
4.链接 gcc hello.o -o hello_elf
过程:
1:预处理
将.c 中的头文件展开、宏展开,生成.i文件
预处理阶段:预处理器(cpp)根据以字符#开头的命令,修改原始的C程序。比如hello.c中第一行的#include命令告诉预处理器读取系统头文件stdio.h的内容,并把它直接插入程序文本中,结果就得到了另一个C程序,通常是以.i作为文件扩展名。
2:编译
将预处理之后的.i文件,生成 .s 汇编文件
编译阶段。编译器(ccl)将文本文件hello.i翻译成文本文件hello.s,它包含一个汇编语言程序。汇编语言程序中的每条语句都以一种标准的文本格式确切的描述了一条低级机器语言指令。
3、汇编
将.s 汇编文件,生成.o 目标文件
汇编阶段。汇编器(as)将hello.s翻译成机器语言指令,把这些指令打包成一种可重定位目标程序的格式,并将结果保存在目标文件hello.o中。hello.o文件是一个二进制文件,它的字节编码是机器语言指令而不是字符,如果我们在文本文件中打开hello.o文件,看到的将是一堆乱码。
4、链接
将.o文件链接,生成目标文件
链接阶段。链接器(ld)负责处理合并目标代码,生成一个可执行目标文件,可以被加载到内存中,由系统执行。
宏定义#define-宏定义
宏是一种批量处理的称谓,是在预编译的时候进行替换。一般用#define定义宏
格式:
- #define 标识符 变量——不带参宏
- #define 标识符(形参) 运算符——带参宏
注意事项:
- 宏的好处:只要修改宏定义,其他地方在预编译的时候就会重新替换。
- 作用范围:从定义的地方到本文件末尾。
- 标识符和形参之间不能有空格,其余地方要有空格。预处理在末尾不加分号 ;
- 不带参宏的标识符直接替代标识符后面的数值——字符型(串)、整型、浮点型等
- 带参宏的 标识符(形参) 替代标识符后面的的运算符的结果,其中形参要进行赋值。带参宏就类似于一个简单的函数
- 如果想在终止宏可以用#undef 例如#undef PI表示终止 PI的作用,但实用性不强,一般用不到
- 宏定义只是简单的替换,不会自动加括号(即按照括号-乘除-加减的顺序计算),带参宏容易出现bug,所以一般默认要加括号
宏的优缺点
优点:1.代码复用性 2.提高性能
缺点:1.不可调试(预编译阶段进行了替换) 2.无类型安全检查 3.可读性差,容易出错
案例1:简单的宏使用
案例1分析:三个宏定义,第一个求圆的半径,PI直接替代3.1415926。第二个求ab之积,78代表x 46代表y,其中x和y并不能直接单独使用(如不能直接x=78和scanf输入x)。第三个是条件运算符,其中78和467分别代表ab,同样XO(a,b)只能以数据的形式使用
案例2:使用define时易出现的错误
#define SUM(a,b) a*b //代码1
#define SUM(a,b) (a)*(b) //代码2
#define SUM(a,b) ((a)*(b)) //代码3
******************************************
printf("%d\n",SUM(7,8)); //输出1
printf("%d\n",SUM(7,8+1)); //输出2
printf("%d\n",64/SUM(7+1,8)); //输出3
案例分析:代码1、2、3看似效果一样,实则大不相同。输出结果如下
输出1 | 输出2 | 输出3 | |
代码1 | 56---7*8 | 57---7*8+1 | 17.14…---64/7+1*8 |
代码2 | 56---(7)*(8) | 63---(7)*(8+1) | 64---64/(7+1)*8 |
代码3 | 56---((7)*(8)) | 63---((7)*(8+1)) | 1---64/((7+1)*(8)) |
所以通过案例2可以发现带参宏最好要带括号,以防与预期结果有出入
带参宏和函数的区别带参宏被调少次就会展开多少次,执行代码的时承数调用的过程,不需要压栈弹栈。所以带参宏是浪费了空间节省时间。
带参函数代码只有一份,存在代码段,有个调用的过程,调用的时候去代码段取指令,要压栈弹栈。所以说带参函数是浪费了时间节省了空间。
带参函数的形参是有类型的,带参宏的形参没有类型名
#define的特殊用法#define还可以重命名变量名
#define 新变量名称 旧变量名称 #define entrance ent
#define还可以给函数重命名,例如将scanf当作scanf_s使用
#define scanf scanf_s
选择性编译选择性编译是在预编译阶段执行的操作
- 方法一
#ifdef AAA
代码段一
#else
代码段二
#endif
如果在当前.c #ifdef 上边宏(define)定义过AAA,就编译代码段一,否则编译代码段二
注意区分选择性编译和 if else 语句的区别,if else 语句都会被编译,通过条件选择性执行代码
而选择性编译,只有一块代码被编译
代码示例:
#define AAA
int main(int argc, char *argy[])
{
#ifdef AAA
printf("hello kitty!!\n");
#else
printf("hello tom\n");
#endif
return 0;
}
- 方法二
#ifndef AAA
代码段一
#else
代码段二
#endif
和方法一互补,常用于防止头文件重复包含
- 方法三
#if 表达式
代码段一
#else
代码段二
#endif
如果表达式为真,编译代码段一,否则编译代码段二
这种方式一般用于注释多行代码,用法如下:
#if 0 //非零即为真
代码段一
#else
代码段二
#endif
宏的补充- C语言宏定义为何不用分号结束: 因为#define语句属于编译预处理,它不属于c语言语句。c语言规定每一条语句后面要加上分号,但编译预处理不是c语言语句,所以不必遵守此规定。
- 在定义宏时,如果一行写入的数据太长,会影响阅读,这时可以在需要换行的地方加入反斜杠(\),然后在\后换行。 比如下方的代码1和代码2是没有区别的。
代码1
代码1
#define output(x) printf("the var's \
value is %d",\
x)
代码2
#define output(x) printf("the var's value is %d", x)
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
本文名称:第5章-宏-创新互联
浏览地址:http://myzitong.com/article/djidjh.html