C语言学习第六天-预处理命令

以#号开头的命令称为预处理命令。

C语言源文件要经过编译、链接才能生成可执行程序:

编译(Compile)会将源文件(.c文件)转换为目标文件。对于 VC/VS,目标文件后缀为.obj;对于GCC,目标文件后缀为.o。编译是针对单个源文件的,一次编译操作只能编译一个源文件,如果程序中有多个源文件,就需要多次编译操作。

链接(Link)是针对多个文件的,它会将编译生成的多个目标文件以及系统中的库、组件等合并成一个可执行程序。

在编译之前对源文件进行简单加工的过程,就称为预处理(即预先处理、提前处理)。

预处理主要是处理以#开头的命令,例如#include<stdio.h>等。预处理命令要放在所有函数之外,而且一般都放在源文件的前面。

预处理是C语言的一个重要功能,由预处理程序完成。对一个源文件进行编译时,系统将自动调用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。

#if、#elif、#endif 就是预处理命令,它们都是在编译之前由预处理程序来执行的。

#include

#include叫做文件包含命令,用来引入对应的头文件(.h文件)。#include 也是C语言预处理命令的一种。

#include 的处理过程很简单,就是将头文件的内容插入到该命令所在的位置,从而把头文件和当前源文件连接成一个源文件,这与复制粘贴的效果相同。

#include 的用法有两种,如下所示:

#include <stdHeader.h>

#include "myHeader.h"

使用尖括号<>和双引号""的区别在于头文件的搜索路径不同:

使用尖括号<>,编译器会到系统路径下查找头文件;

而使用双引号"",编译器首先在当前目录下查找头文件,如果没有找到,再到系统路径下查找。

使用双引号比使用尖括号多了一个查找路径,它的功能更为强大。

stdio.h 和 stdlib.h 都是标准头文件,它们存放于系统路径下,所以使用尖括号和双引号都能够成功引入;而我们自己编写的头文件,一般存放于当前项目的路径下,所以不能使用尖括号,只能使用双引号。

使用尖括号来引入标准头文件,使用双引号来引入自定义头文件(自己编写的头文件),这样一眼就能看出头文件的区别。

关于 #include 用法的注意事项:

1. 一个 #include 命令只能包含一个头文件,多个头文件需要多个 #include 命令。

2. 同一个头文件可以被多次引入,多次引入的效果和一次引入的效果相同,因为头文件在代码层面有防止重复引入的机制。

3. 文件包含允许嵌套,也就是说在一个被包含的文件中又可以包含另一个文件。

「在头文件中定义定义函数和全局变量」这种认知是原则性的错误!不管是标准头文件,还是自定义头文件,都只能包含变量和函数的声明,不能包含定义,否则在多次引入时会引起重复定义错误。

宏定义

#define 叫做宏定义命令,它也是C语言预处理命令的一种。所谓宏定义,就是用一个标识符来表示一个字符串,如果在后面的代码中出现了该标识符,那么就全部替换成指定的字符串。

宏定义的一般形式为:

#define  宏名  字符串

#表示这是一条预处理命令,所有的预处理命令都以 # 开头。宏名是标识符的一种,命名规则和变量相同。字符串可以是数字、表达式、if 语句、函数等。

这里所说的字符串是一般意义上的字符序列,不要和C语言中的字符串等同,它不需要双引号。

对 #define 用法的几点说明:

1. 宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单粗暴的替换。字符串中可以含任何字符,它可以是常数、表达式、if 语句、函数等,预处理程序对它不作任何检查,如有错误,只能在编译已被宏展开后的源程序时发现。

2. 宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起替换。

3. 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用#undef命令。

4. 代码中的宏名如果被引号包围,那么预处理程序不对其作宏代替。

5. 宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名,在宏展开时由预处理程序层层代换。

6. 习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母。

7. 可用宏定义表示数据类型,使书写方便。

应注意用宏定义表示数据类型和用 typedef 定义数据说明符的区别。宏定义只是简单的字符串替换,由预处理器来处理;而 typedef 是在编译阶段由编译器处理的,它并不是简单的字符串替换,而给原有的数据类型起一个新的名字,将它作为一种新的数据类型。

带参数的宏定义

C语言允许宏带有参数。在宏定义中的参数称为“形式参数”,在宏调用中的参数称为“实际参数”,这点和函数有些类似。

对带参数的宏,在展开过程中不仅要进行字符串替换,还要用实参去替换形参。

带参宏定义的一般形式为:

#define 宏名(形参列表) 字符串

在字符串中可以含有各个形参。

带参宏调用的一般形式为:

宏名(实参列表);

对带参宏定义的说明

1. 带参宏定义中,形参之间可以出现空格,但是宏名和形参列表之间不能有空格出现。

2. 在带参宏定义中,不会为形式参数分配内存,因此不必指明数据类型。而在宏调用中,实参包含了具体的数据,要用它们去替换形参,因此实参必须要指明数据类型。

3. 在宏定义中,字符串内的形参通常要用括号括起来以避免出错。对于带参宏定义不仅要在参数两侧加括号,还应该在整个字符串外加括号。

带参宏定义和函数的区别

带参数的宏和函数很相似,但有本质上的区别:宏展开仅仅是字符串的替换,不会对表达式进行计算;宏在编译之前就被处理掉了,它没有机会参与编译,也不会占用内存。而函数是一段可以重复使用的代码,会被编译,会给它分配内存,每次调用函数,就是执行这块内存中的代码。

条件编译

#if、#elif、#else 和 #endif 都是预处理命令。

这些操作都是在预处理阶段完成的,多余的代码以及所有的宏都不会参与编译,不仅保证了代码的正确性,还减小了编译后文件的体积。

这种能够根据不同情况编译不同代码、产生不同目标文件的机制,称为条件编译。条件编译是预处理程序的功能,不是编译器的功能。

#if 的用法

#if 用法的一般格式为:

#if 整型常量表达式1

        程序段1

#elif 整型常量表达式2

        程序段2

#elif 整型常量表达式3

        程序段3

#else

        程序段4

#endif

如常“表达式1”的值为真(非0),就对“程序段1”进行编译,否则就计算“表达式2”,结果为真的话就对“程序段2”进行编译,为假的话就继续往下匹配,直到遇到值为真的表达式,或者遇到 #else。这一点和 if else 非常类似。

需要注意的是,#if 命令要求判断条件为“整型常量表达式”,也就是说,表达式中不能包含变量,而且结果必须是整数;而 if 后面的表达式没有限制,只要符合语法就行。这是 #if 和 if 的一个重要区别。#elif 和 #else 也可以省略。

#ifdef 的用法

#ifdef 用法的一般格式为:

#ifdef  宏名

        程序段1

#else

        程序段2

#endif

如果当前的宏已被定义过,则对“程序段1”进行编译,否则对“程序段2”进行编译。也可以省略 #else。

#ifndef 的用法

#ifndef 用法的一般格式为:

#ifndef  宏名

        程序段1

#else

        程序段2

#endif

与 #ifdef 相比,仅仅是将 #ifdef 改为了 #ifndef。它的意思是,如果当前的宏未被定义,则对“程序段1”进行编译,否则对“程序段2”进行编译,这与 #ifdef 的功能正好相反。

需要注意的是,#if 后面跟的是“整型常量表达式”,而 #ifdef 和 #ifndef 后面跟的只能是一个宏名,不能是其他的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值