C语言预处理详解

server/2024/12/2 6:09:38/

一、预定义符号

C语言设置了一些预定义符号,可以直接使用,预定义符号是在预处理期间处理的。

__FILE__         //进行编译的源文件

__LINE__         //文件当前的行号

__DATE__         //文件被编译的日期

__TIME__         //文件被编译的时间

__STDC__         //如果编译器遵循ANSI C,其值为1,否则未定义

注意打印格式。

二、#define定义常量

基本语法:#define name stuff

举例:

#define MAX 1000                    //用MAX表示1000

#define REG register               //为 register这个关键字,创建一个简短的名字REG

#define do_forever for(;;)         //用更形象的符号来替换一种实现(死循环)

#define CASE break;case       //在写case语句的时候自动把 break写上

#define DEBUG_PRINT printf("file:%s\nline:%d\n \

date:%s\ntime:%s\n" ,\

__FILE__,__LINE__ , \

__DATE__,__TIME__ )

// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。

三、#define定义宏

#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)定义宏(define macro)。

申明方式:#define name( parament-list ) stuff

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

注意:参数列表的左括号必须与name紧邻,如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。

举例对数值表达式进行求值的宏定义:

#define SQRT(x) ( ( x ) + ( x ) )

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

四、带有副作用的宏参数

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

如:

y = x + 1;

y = x++;

两句代码虽然都实现了将x+1的值赋给y但是,第二句改变了x本身的值,这就是带有副作用的。

对于传参的函数,还有有参数的宏需要注意的是:函数的传参是参数部分计算出结果后,将结果传给形参;而宏的参数是替换进去的,不会发生计算,所以前后可能会导致变化。

五、宏替换的规则

(1)在调用宏时,首先对参数进行检查,看看是否包含任何由 #define 定义的符号。如果有,它们就会首先被替换。

(2)替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。

(3)再次对结果文件进行扫描,看看它是否包含任何由 #define 定义的符号。如果有,就重复上述处理过程。

注意:

(1)宏参数和 #define 定义中可以出现其他 #define 定义的符号。但是对于宏,不能出现递归。

(2)当预处理器搜索 #define 定义的符号的时候,字符串常量的内容并不被搜索。

六、宏和函数的对比

属性#define 定义宏函数
代码长度每次使用时,宏代码都会被插入到程序的序列中。除了非常小的宏之外,程序的长度会因为宏代码的插入而大幅度增长函数代码只出现于一个地方;每次使用这个函数时,都调用那个地方的同一份代码
执行速度更快存在函数的调用和返回的额外开销,所以相对慢一些
操作符优先级宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则相邻操作符的优先级可能会产生不可预料的后果函数参数只在函数调用的时候求值一次,求值的结果值传递给函数。表达式的求值结果容易预测
带有副作用的参数参数可能被替换到宏体中的多个位置,如果宏的参数被多次计算,带有副作用的参数求值可能会产生不可预料的结果函数参数只在传参的时候求值一次,结果容易控制
参数类型宏的参数与类型无关,只要对参数的操作是合法的,它就可以使用于任何参数类型函数的参数是与类型有关的,如果参数类型不同,就需要不同的函数,即使他们执行的任务是相同的
调试宏是不方便调试的函数是可以逐语句调试的
递归宏是不能递归的函数是可以递归的

七、#和##

1、#

# 运算符将宏的一个参数转换为字符串字面量。它仅允许出现在带参数的宏的替换列表中。
# 运算符所执行的操作可以理解为” 字符串化 “。

举例:

需要注意的是:C语言支持多个字符串拼接打印。

2、##

## 可以把位于它两边的符号合成一个符号,它允许宏定义从分离的文本片段创建标识符。## 被称为记号粘合

这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。

举例:

八、命名约定

命名习惯:

(1)把宏名全部大写

(2)函数名不要全部大写

九、#undef

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

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

十、命令行定义

许多C的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。

十一、条件编译

如果我们要将一条语句(一组语句)编译或者放弃可以通过条件编译指令

常见的条件编译指令:

1.

#if 常量表达式

//...

#endif //常量表达式由预处理器求值。

如: #define __DEBUG__ 1

#if __DEBUG__

//..

#endif

2.多个分支的条件编译

#if 常量表达式

//...

#elif 常量表达式

//...

#else

//...

#endif

3.判断是否被定义

#if defined(symbol)        

#if !defined(symbol)        //上一条的否定

#ifdef symbol

#ifndef symbol                //上一条的否定

4.嵌套指令

#if defined(OS_UNIX)

        #ifdef OPTION1

                unix_version_option1();

        #endif

        #ifdef OPTION2

                unix_version_option2();

        #endif

#elif defined(OS_MSDOS)

        #ifdef OPTION2

                msdos_version_option2();

        #endif

#endif

十二、头文件的包含

1、本地文件包含

#include "filename"

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

2、库文件包含

#include <filename>

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

需要注意的是:理论上对于库文件也是可以用 " " 的形式包含,但是这样做查找的效率就会降低,并且这样也不容易区分是本地文件还是库文件。

3、嵌套文件包含

可以用条件编译的方法来解决头文件被重复引入的问题:

#ifndef __TEST_H__       

#define __TEST_H__

//头文件的内容

#endif //

第一次进入之前 __TEST_H__ 还未被定义,所以可以引入头文件,但是引入头文件的同时定义了__TEST_H__ 之后就不能再次引入头文件了。

十三、其他预处理指令

如offsetof

在 C 语言中, offsetof 是一个宏,用于获取结构体成员相对于结构体起始地址的偏移量。它在 <stddef.h> 头文件中定义。

(1)(*type)0首先将整数0强制类型转化为结构体指针类型,用来代表起始地址为0,是一个空指针。

(2)((type*)0)->member  通过创建的空指针访问结构体type中的成员member,编译器会根据成员member在结构体中的位置来计算地址。

(3)&((type*)0)->member  由于起始地址为0,所以取出的地址就是该成员member的偏移量。

推荐自主学习,培养自主学习的能力。

#error

#pragma

#line

……


http://www.ppmy.cn/server/146633.html

相关文章

网络安全问题与大忌

一、不堪一击的根服务器 互联网的唯一致命弱点就是它完全依赖于使用根服务器的域名系统(DNS)&#xff0c;根服务器掌握着国际域名&#xff08;如 .com, .net, .org&#xff09;的所有授权细节。 位于全球的网络结构的核心中共有13台这种根服务器。这个服务器网络由命名和数字…

Ps:存储 Adobe PDF - 输出

在 Adobe Photoshop 中&#xff0c;将图像保存为 PDF 文件时&#xff0c;“存储为 Adobe PDF” 对话框中的“输出” Output选项卡允许控制颜色转换和 PDF/X 设置&#xff0c;这对于确保颜色准确再现和符合印刷标准至关重要。 颜色 Color 颜色转换 Color Conversion 此设置决定在…

技术总结(四十)

MongoDB 的存储结构是什么&#xff1f; MongoDB 的存储结构区别于传统的关系型数据库&#xff0c;主要由如下三个单元组成&#xff1a; 文档&#xff08;Document&#xff09;&#xff1a;MongoDB 中最基本的单元&#xff0c;由 BSON 键值对&#xff08;key-value&#xff09;…

接口性能优化宝典:解决性能瓶颈的策略与实践

目录 一、直面索引 &#xff08;一&#xff09;索引优化的常见场景 &#xff08;二&#xff09;如何检查索引的使用情况 &#xff08;三&#xff09;如何避免索引失效 &#xff08;四&#xff09;强制选择索引 二、提升 SQL 执行效率 &#xff08;一&#xff09;避免不必…

Web3的核心技术解析:从区块链到智能合约的全景探索

随着互联网技术的不断演进&#xff0c;Web3作为新一代互联网的发展方向&#xff0c;吸引了广泛的关注。它以去中心化、用户数据自主和透明性为核心&#xff0c;描绘了一个全新的数字生态。而区块链、智能合约等技术是Web3得以实现的关键支撑&#xff0c;为未来的技术变革提供了…

MySQL8.0 双密码机制:解决应用程序用户不停机修改密码问题

点击上方蓝字关注我 在数据库管理中&#xff0c;定期更新密码是确保系统安全的重要手段。然而&#xff0c;如何在不影响现有连接的情况下平滑地切换密码&#xff0c;避免系统停机&#xff0c;始终是一个挑战。MySQL 8.0 引入的“双密码”机制为这种需求提供了有效的解决方案&am…

wordpress 中添加图片放大功能

功能描述 使用 Fancybox 实现图片放大和灯箱效果。自动为文章内容中的图片添加链接&#xff0c;使其支持 Fancybox。修改了 header.php 和 footer.php 以引入必要的 CSS 和 JS 文件。在 functions.php 中通过过滤器自动为图片添加 data-fancybox 属性。 最终代码 1. 修改 hea…

【故障处理系列--业务官网无法打开】

故障处理 故障现象&#xff1a;客户反馈我们的业务官网无法打开&#xff0c;我这里测试一会可以一会不可以且post请求我们的官网接口是失败的 2、排查思路 2.1. 检查后端服务 kubectl get pod -n pcas后端pod的状态运行是正常的 2.2. 检查网关pod&#xff0c;查看是否接收…