关于C语言的预处理简单介绍

C语言的预处理

预处理命令的介绍

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

  1. 编译。将C语言文件转换为目标文件。对于VS/VC来说,目标文件的后缀为.obj;对于gcc来说,目标文件的后缀为.o
  2. 链接。将编译生成的多个文件以及系统中的库、组件等合并成一个可执行程序。

预处理:在编译之前,首先对文件进行处理的过程。

预处理主要是处理以#开头的命令。预处理命令要求放在所有函数之外,而且一般都放在源文件的最前面。所有的预命令都以#开头。

编译器会将预处理的结果保存到和源文件同名的.i文件中,并且可以用文本编辑器直接查看。

C语言提供了多种预处理功能,包括宏定义、文件包含、条件编译等,合理地使用这些命令将有助于提升程序的健壮性,使编写的程序便于阅读、修改、移指和调试,也有利于模块化程序设计。

例子:Windows系统和Linux系统下的相同函数sleep()使程序休眠5秒,Windows系统下位于windows.h文件中,Linux系统下位于unistd.h文件。

#include <stdio.h>
#if _WIN32
#include <windows.h>
#elif __linux__
#include <unistd.h>
#endif


int main(){
	#if _WIN32
	// Windows系统下休眠函数首字母大写且传入的参数的单位为毫秒
	Sleep(5000);
	#elif __linux__
	// Linux系统下休眠函数首写字母小写且传入的参数的单位为秒
	sleep(5);
	#endif
	printf("运行结束");
	return 0;
}
文件包含命令#include

它有两种引用方法,分别为

  1. 包含的文件使用<>

    它表示在编译时,编译器会到系统路径下查找头文件

  2. 包含的文件使用""

    它表示在编译时,编译器会先在本地路径下查找该头文件;如果找不到,再去系统路径下进行查找

引用方法的应用场景

  1. 第一种引用方法通常用来引用标准头文件
  2. 第二种引用方法通常用来引用自己编写的头文件

引用的注意事项

  1. 一个#include只能引用一个头文件;多个头文件需要多次引用。
  2. 同一个头文件可以被引入多次,但是引入多与引入一次是相同的。这是因为,C语言有防止有文件被重复包含的机制。
  3. 文件包含允许被嵌套。即一个头文件中可以引用另一个头文件。

无论是标准的头文件,还是自己定义的头文件。头文件中只能包含函数的声明,而不能有函数的定义。

扩展:函数的声明与定义
  1. 函数的声明

    对编译系统的一个说明:对定义的函数的返回值类型的说明,以通知系统在本函数中所调用的函数是什么类型。函数的声明是一个说明语句,必须以分号结束。

  2. 函数的定义

    一个完整的函数单元,包含函数类型、函数名、形参及形参类型、函数体等。在程序中,函数定义有且只能有一次。

C语言的宏定义

C语言的宏定义也是C语言预处理命令的一种,宏定义表示如下

#define N 100
/* 说明
这是一条宏定义命令
N为宏名
100为宏的内容
在预处理阶段,预处理器会使用宏定义中的字符串对程序中所有出现的“宏名”进行代换,这称为“宏替换”或“宏展开”
*/

宏定义的一般形式如下

#define 宏名 字符串
/* 参数说明
宏名:标识符的一种,命名规则和变量名相同
字符串:可以是数字、表达式、if语句、函数等
*/

关于宏定义的几点说明

  1. 宏定义的本质是用宏名来表示一个字符串,在宏展开时,又用该字符串取代宏名。预处理程序不会对宏定义中的字符串进行任何检查,如有错误,只能在编译已被展开后的源程序时发现。

  2. 宏定义不是说明语句,定义的最后不用加分号。如果加上,会连同分号一起替换。

  3. 宏定义的默认作用域是整个源程序。如果要终止其作用域,可用命令#undef结束,命令的一般形式如下

    #define name str
    /*
    代码段
    */
    #undef name
    
  4. 代码中的宏名如果被引号包围,则预处理程序不会对其作宏替换

  5. 允许嵌套定义

  6. 习惯上,使用大写字母命名宏名

  7. 可以用来表示数据类型

    与typedef定义的区别

    • 宏定义只是简单的字符串替换,由预处理器完成
    • typedef是在编译阶段由编译器处理的
C语言中带参数的宏定义

相当于Python的lambda表达式。在宏定义中的参数称为“形式参数”,在宏调用中的参数称为“实际参数”。

带参宏定义的一般形式如下

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

带参宏定义的一般调用形式如下

宏名(实参列表)

示例

#include <stdio.h>
#define MAX(a, b) a > b ? a : b

int main()
{
    int x = 10;
    int y = 5;
    int max_num = MAX(x, y);
    printf("x和y中较大的值为%d\n", max_num);
    return 0;
}

关于带参宏定义的几点说明

  1. 宏名与形参列表之间不可以出现空格。
  2. 在定义形参中,不会为形参分配内存,因此无需指明形参的类型。但是在调用宏时,需要指明传入的实参类型。
  3. 宏定义不同于函数,不存在值传递的问题;当传递的是一个表达式时,函数会先计算出值,再把值传递进去,而宏定义则会直接进行替换再计算。
  4. 为了确保程序的原含义不变,通常要对整体字符串加一个括号,也要对其中每个表示变量的字符加一个括号。
C语言中宏定义的特殊用法
使用#将宏参数转变为字符串

示例

#include <stdio.h>
#define STR(s) #s
// 这里的#s中的#会将s字符串化

int main()
{
    // 特别的,如果原有的s即为字符串,则会对该字符串的引号进行转义
    // printf("%s\n", STR("Hello,World"));
    // 宏展开为\"Hello输出的结果为"Hello,World"
    printf("%s\n", STR(Hello,World));
    return 0;
}
使用##将宏参数进行连接

将宏参数与其他的串连接起来

示例

#include <stdio.h>
#define CON(a, b) a##b##00

int main(){
	// 输出的结果为123400
	printf("%d\n", CON(12, 34));
	return 0;
}

C语言中的几个预定义的宏

各个宏的名称及用法表示如下

名称类型作用
LINE整型表示当前源代码的行号
FILE字符串型表示当前源文件的名称
DATE字符串型表示当前编译的日期
TIME字符串型表示当前编译的时间
STDC当要求程序严格遵循ANSI C标准时,该标识符被赋值为1
条件编译

定义:能够根据不同情况编译不同代码、产生不同目标文件的机制称为条件编译。

#if的用法

它的一般形式如下所示

#if 表达式1
// 代码段
#elif 表达式2
// 代码段
// ...
#elif 表达式n
// 代码段
#else
// 代码段

它的用法与一般的程序语言中的if用法相同。如果表达式的值为1,则进入相应的选择结构中。

#ifdef的用法

它的一般形式如下所示

#ifdef 宏名
// 代码段
#else
// 代码段
#endif

其中的else部分可省略,但是最后的#endif不可以省略

#ifndef的用法

它的一般形式与#ifdef相同,不同之处在于:它是#ifdef的取反形式

#error指令的用法

使用方式:在编译期间产生错误信息,并阻止程序的编译

它的一般形式如下所示

#error error_message

示例

#ifdef WIN32
#error This programme cannot compile at Windows Platform
#endif

当该程序在Linux系统下进行编译的时候,就会报错并终止编译

**注:**error_message不需要加引号,如果加上,会连同引号一块输出

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值