C语言进阶-程序环境和预处理

目录

​编辑

翻译环境

编译过程

预定义符号

#define

#define 定义标识符

#define 定义宏

注意

例1

例2

提醒

#和##

##的作用

​编辑

#的作用

​编辑

带副作用的宏参数

预处理结果

输出结果

宏和函数对比

宏的缺点

命名约定

#undef

文件包含

本地文件包含

库文件包含

如何避免嵌套文件包含

 法1

法2

注意


翻译环境

组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)。

每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。

链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人 的程序库,将其需要的函数也链接到程序中 

编译过程

1.预处理 gcc -E test.c -o test.i

预处理完成之后就停下来,预处理之后产生的结果都放在test.i文件中。

2. 编译 gcc -S test.c

编译完成之后就停下来,结果保存在test.s中。

3. 汇编 gcc -c test.c

汇编完成之后就停下来,结果保存在test.o中。

预定义符号

__FILE__      //进行编译的源文件
__LINE__     //文件当前的行号
__DATE__    //文件被编译的日期
__TIME__    //文件被编译的时间
__STDC__    //如果编译器遵循ANSI C,其值为1,否则未定义

#define

#define 定义标识符

语法:

         #define name stuff

在define定义标识符的时候,要不要在最后加上 ;

 

建议不要加上 ;   这样容易导致问题

如下代码

#define MAX 1000;
#define MAX 1000

if(condition)
 max = MAX;
else
 max = 0;

出现了两个;的情况  导致程序无法运行

#define 定义宏

#define name( parament-list ) stuff

其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中

注意

1.参数列表的左括号必须与name紧邻。

2.如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分

例1

#define SQUARE( x ) x * x

int a = 5;
printf("%d\n" ,SQUARE( a + 1) );

乍一看,你可能觉得这段代码将打印36这个值。 事实上,它将打印11. 为什么?

替换文本时,参数x被替换成a + 1,所以这条语句实际上变成了:

printf ("%d\n",a + 1 * a + 1 );

例2

#define DOUBLE(x) (x) + (x)


int a = 5;
printf("%d\n" ,10 * DOUBLE(a));

看上去,好像打印100,但事实上打印的是55

替换后:

printf ("%d\n",10 * (5) + (5));

提醒

用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。

#和##

##的作用

  从上面例子可以总结出,##的作用就是把2个宏参数连接为1个数。

当然也可以实现字符串的连接,如下:

#的作用

所以,可以总结出#的作用,#的作用就是讲#后面的宏参数进行字符串的操作,也就是将#后面的参数两边加上一对双引号使其成为字符串。

摘自:http://t.csdnimg.cn/I5u2T

带副作用的宏参数

当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能 出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果

x+1;//不带副作用
x++;//带有副作用


#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
...
x = 5;
y = 8;
z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);

以上代码输出的结果是什么?

预处理结果

z = ( (x++) > (y++) ? (x++) : (y++));

输出结果

x=6 y=10 z=9

宏和函数对比

宏通常被应用于执行简单的运算

如在两个数中找出较大的一个

#define MAX(a, b) ((a)>(b)?(a):(b))

为什么不用函数来完成这个任务

原因有二:

1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。 所以宏比函数在程序的规模和速度方面更胜一筹。

2. 更为重要的是函数的参数必须声明为特定的类型。 所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以 用于>来比较的类型。

宏是类型无关的。

宏的缺点

1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序 的长度。

2. 宏是没法调试的

3. 宏由于类型无关,也就不够严谨。

4. 宏可能会带来运算符优先级的问题,导致程容易出现错。

命名约定

把宏名全部大写

函数名不要全部大写

#undef

这条指令用于移除一个宏定义。

#undef NAME
//如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除

文件包含

本地文件包含

#include "filename"

查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标 准位置查找头文件。

如果找不到就提示编译错误。

库文件包含

#include <filename.h>

查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。

这样是不是可以说,对于库文件也可以使用 “” 的形式包含?

答案是肯定的,可以。

但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了

如何避免嵌套文件包含

 法1

#pragma once

法2

条件编译

#ifndef _TEST_H_
#define _TEST_H_
//头文件的内容
#endif   //_TEST_H_


注意

#ifndef 是if not define的简写

当第一次包含test.h时,由于没有定义_TEST_H,条件为真,这样就会包含(执行)#ifndef _TEST_H和#endif之间的代码,当第二次包含test.h时前面一次已经定义了_TEST_H,条件为假,#ifndef _TEST_H和#endif之间的代码也就不会再次被包含,这样就避免了重定义。

主要用于防止重复定义宏和重复包含头文件

  1. 程序中_TEST_H_为预处理变量,书写格式一般是头文件名大写,前后加下划线,".“用下划线”_"代替.
  2. 预处理变量一般有两种状态:已定义或未定义
  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

比特大魔One

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值