浅谈C语言预处理

news2024/9/21 1:31:48

文章目录

  • 预处理
    • 1、预定义符号
    • 2、#define定义标识符和宏
      • A、#define定义标识符
      • B、#define定义宏
      • a、宏的定义
      • b、宏的使用
      • c、宏和函数
    • 3、条件编译
    • 4、头文件包含
      • A、两种包含形式
      • B、防止头文件被重复包含

预处理

什么是预处理?预处理是C语言编译的三个过程(预处理、编译、汇编)中的第一步,它主要进行的是一些文件操作,如#define定义的标识符和宏的替换,头文件的包含和注释的删除,等等。

以下,我们将对预处理中的一些操作进行一个较为详细的了解。


1、预定义符号

预定义符号主要有以下5个:
1. __ FILE __: 指明进行编译的源文件
2. __ LINE __:文件中当前语句的行数
3. __ DATE __:文件被编译的日期
4. __ TIME __:文件被编译的时间
5. __ STDC __:即“标准C”,如果当前编译器遵循ANSI C标准,则其值为1,否则未定义

以下我们来使用这几个预定义符号:
在这里插入图片描述
相应的输出结果为:
在这里插入图片描述

关于__ STDC __的使用,我们用VS编译器进行测试:
在这里插入图片描述
我们可以看到,在VS下,__STDC__是未定义的标识符,,说明VS编译器并不严格遵守ANSI C标准。


2、#define定义标识符和宏

A、#define定义标识符

#define定义标识符的用途很广,基本的格式就是:#define A B。其实可以简单的理解成将B重命名为A,A的作用与B等同。

以下仅举几个例子:
无
这里的print就是起到打印haha外加换行的作用
无
特别注意:#define的定义,最后不用加分号

B、#define定义宏

宏的基本格式:宏名、宏参和宏的基本内容

a、宏的定义

在这里插入图片描述
ADD是宏名,ADD后括号内的是宏参,之后的x+y是宏的具体内容

可以定义宏,当然我们也可以取消相应的宏的定义,使用#undef
在这里插入图片描述

b、宏的使用

在这里插入图片描述
这样输出结果就为7

在宏的定义中我们可以发现,宏的内容部分,我们加了三个括号,这是为什么呢?这是为了避免一些由于运算优先级可能会造成的问题。

我们看不加括号的情况:
在这里插入图片描述

这时,不了解宏的话,你可能会认为输出的是16,但实际上输出的是11。

要理解这个,就需要了解宏参的替换问题。

加括号:在宏的具体内容中,一定要给每一个宏参都加上括号,以及宏的具体内容整体也要加上括号。
前者主要是由于宏在传参时,进行的是参数的替换,即如果以一个表达式为参数,参数替换时就将这个表达式替换进去,而不是这个表达式的值,这样在实际中可能就会存在一个运算优先级的问题;后者加括号也是为了将宏视作一个整体,避免一些运算优先级的问题。

我们将宏ADD替换后,实际得到的是:
在这里插入图片描述
宏参是直接替换进去的,而不是将表达式的运算结果传递进去的。因此,在定义宏时,保证准确性,我们最一定要学会加括号

c、宏和函数

通过上面对宏的探讨,可以发现宏和函数有惊人的相似之处,很多函数的功能,我们都可以通过宏来实现。
以下我们将从多个方面来比较宏和函数。

  1. 命名:宏的命名通常要全大写(存在例外,如offsetof宏),而函数的命名通常不要全大写
  2. 参数类型角度:宏的参数是没有限制的,更加自由,但也缺少严谨;相比之下,函数只能传特定类型的参数,限制更多,但也更加严谨
  3. 参数传递角度:宏的参数,准确来说是参数的替换,参数是表达式也一样进行替换;而函数的传参,如果是表达式的话,则是把表达式的值传给相应的形参
  4. 代码长度角度:宏的使用,实际上进行的是代码的替换,如果宏自身较长,而宏的使用也比较多,则会导致预处理后,程序长度大幅增加;而对于函数,每次函数的调用,都是调用同一处函数的代码,所以不存在宏的问题
  5. 调试角度:由于宏在预处理中会进行替换,所以宏是没有办法进行调试的,而函数则可以正常调试
  6. 效率角度:从代码执行的效率来说,如果本身要实现的功能较为简单,使用宏,程序执行的效率会更高;而使用函数的话,可能执行调用函数和函数返回的代码所花费的时间已远远大于实际运算工作所花的时间,效率较低

综述: 综合来看,代码功能相对简单,使用宏可能更好;代码功能相对复杂,使用函数可能更好


3、条件编译

什么是条件编译?顾名思义,条件编译是只有在满足一定条件的情况下才会进行编译,即条件编译的代码只有在满足相应条件时,才会正常执行
以下介绍一些用于条件编译的语句:

  1. #if+#endif
    在这里插入图片描述
    只有当i=5时,才会打印i

  2. #ifdef+#endif
    在这里插入图片描述
    只有当定义了标识符MAX时,才会打印 i

    这种形式还可以有如下写法:
    在这里插入图片描述
    两种写法作用是相同的
    当然,两种写法也都有相应的否定形式
    在这里插入图片描述
    在这里插入图片描述
    这两种写法意思为:只有在未定义MAX时,才会打印 i

  3. 条件编译的选择形式
    这种情况下的条件编译类似于if-else语句:
    在这里插入图片描述

4、头文件包含

头文件的包含使用#include+头文件,头文件通常用尖括号或双引号进行引用

A、两种包含形式

在这里插入图片描述
这两种情况该如何选择呢?
我们通常都是这么说的:库函数头文件的包含使用尖括号,而程序员自己创建的头文件的包含使用双引号。
但实际上,双引号的覆盖范围是包括了尖括号的。

  1. 双引号引头文件:
    这种情况下,编译器会先到代码所在的路径下找相应的头文件,如果找不到,再去VS的库目录下查找。
  2. 尖括号引头文件:
    这种情况下,编译器直接到VS的库目录下查找。

B、防止头文件被重复包含

我们平时写代码,有时候会重复包含头文件,尤其是对于库函数头文件的重复包含,会在程序预处理后,大大增加程序的长度,所以我们该如何避免头文件被重复包含呢?

以下介绍两种方法:

  1. 第一种方法:
    在这里插入图片描述
    在头文件的第一行添加上述代码,使得该头文件只会被包含一次。

  2. 第二种方法:
    在这里插入图片描述
    使用**#ifndef#endif**,说明其中的代码只有在_TEST_H__未定义时,才能正常编译,而实际上一旦包含该头文件一次,_TEST_H__就已经定义了,因此这样就确保了头文件中的内容只会被包含一次。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2042611.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一口气把halcon的所有运算符说清楚

halcon的运算符大体分以下几类 一:赋值运算符: (1) 赋值(:)(左边的赋值给右边) 二:算术运算符 (1)加()、减(-)、乘(*)、除(/)、求余(%) 三:关系运算符 (3)#、! 不等于 四:逻辑运…

java中运算符的详细知识点

算数运算符 a 先赋值再加1 a 先加1在赋值 --的道理是一样的 赋值运算符 1. - * / % 当两侧数据类型不一致时,可以使用自动类型转换或使用 强制类型转换原则 支持连续赋值 - * / % 不会改变基础类型 测试一下: 比较运算符 运算结果为布尔类型 &#x…

Windows上安装WSL,学习Linux

1. 什么是WSL 先说大白话WSL就是让Windows不安装虚拟机可以额外拥有Linux操作系统,以供学习和测试 WSL(Windows Subsystem for Linux)和WSL2 是微软推出的两个工具,旨在让用户能够在 Windows 操作系统上运行 Linux 的命令行工具…

MySQL数据分析进阶(十四)保护数据库

※食用指南:文章内容为‘CodeWithMosh’SQL进阶教程系列学习笔记,笔记整理比较粗糙,主要目的自存为主,记录完整的学习过程。(图片超级多,慎看!) 【中字】SQL进阶教程 | 史上最易懂S…

Unity游戏开发004:如何在Unity中对物体进行基本操作

Unity游戏开发 “好读书,不求甚解;每有会意,便欣然忘食。” 本文目录: Unity游戏开发 Unity游戏开发前言左侧工具栏概述1. **创建物体**2. **移动(Move)**3. **旋转(Rotate)**4. **缩…

注册数据查询工具

注册数据查询工具:ICANN Lookup

微前端插件 v-micro-app-plugin

v-micro-app-plugin 是一款基于京东MicroApp框架的微前端插件,旨在帮助开发者快速地将微应用集成到不同的系统中,实现高效、灵活的前端模块化开发。以下是详细的使用指南,帮助你快速上手。 微前端插件 v-micro-app-plugin 源码地址&#xff1…

golang 命令行聊天室

实战简介: 基于tcp协议实现功能 服务器端: 接受用户消息和循环转发 对功能命令进行处理(meun查询功能词,changeName改名,online查询在线人数,quit退出) 客户端: 接受服务器发送…

【话题】程序员之路:高效编码与持续成长的平衡之道

目录 程序员如何平衡日常编码工作与提升式学习? 引言 方向一:高效编码习惯与时间管理技巧 方向二:提升式学习的策略 实际案例: 方向三:职业发展与个人成长的和谐共生 实际案例: 程序员如何平衡日常编码工作…

金融业与三方公司网络互联方法

文章目录 云桌面定义类型非持久桌面主要特点和优点 持久桌面主要特点和优点 网络缺点 专线定义特点网络缺点 VPN定义特点网络缺点 零信任定义优点缺点与挑战网络应用最佳实践案例与趋势 互联网加白名单定义应用场景访问流程图优点缺点相关技术 云桌面 定义 云桌面是一种基于云…

工厂间的库存转储

从一个工厂到另一个工厂实施实物物料转储: 一步式程序两步式程序:使用两步式程序进行库存转储可能是因为两个工厂相距甚远,货物在某个特定时间内处于运输状态;或者两个工厂分别由不同的员工负责,每个员工只能过帐本工…

红酒与节日装饰:打造节日氛围的需备品

随着节日的脚步渐渐临近,节日的氛围也愈发浓厚。在这个特殊的时刻,红酒与节日装饰无疑成为了营造节日氛围的需备品。洒派红酒(Bold & Generous)作为定制红酒的品牌,其不同的韵味与节日装饰的精致整合,共…

react笔记:redux

redux状态管理 安装redux:num i redux 新建redux文件夹: store.jscount_reducer.js count_action.js constant.js (常量) 1. store.js文件: // 该文件专门用于暴露一个store对象,整个应用只有一个store对…

MyBatis巧用1=0,再鸡肋也得用

一、问题导向 今天在实现文件上传数据库测试中,发现了一个略微容易被问题。虽然业务层可以约束空字符串的传入。但是测试DAO数据层时,被同事发现,说我:“为什么不加10”。 请看问题。 如果用户账号为空,则有分页输出…

在JavaFx中使用StyledLayerDescriptor(SLD)1.0和1.1版本进行地图美化实践

目录 前言 一、最开始的初始世界 1、默认的样式 二、注册事件让地图实现交互 1、绑定事件及注册处理逻辑 三、地图美化,让地图生动起来 1、POM.xml中引入相应的依赖 2、GeoTools加载不同版本的SLD问题 3、加载1.1.0版本的SLD 四、总结 前言 俗话说&#xff…

超简单理解LSTM和GRU模型

目录 参考资料 RNN在反向传播时容易遭受梯度消失的问题,而梯度是用于更新神经网络权重的关键因子,梯度消失描述的是梯度在时间序列反向传播中逐渐减小的情况。 若梯度过小,它对于网络的学习贡献甚微。结果是,在RNN中,梯…

成功的秘诀:客户推荐推动 SaaS 成功的4个原因

在竞争激烈的SaaS市场中,脱颖而出并非易事。客户推荐合作伙伴关系正是这样一种强大的营销渠道,它利用口碑传播的力量,将满意的客户转变为品牌倡导者,从而显著提升品牌信誉和市场影响力。 您的 SaaS 品牌应该接受客户推荐营销的4 …

进 程

1.进程:进行中的程序。 微观串行,宏观并行。 程序的一次执行过程 进程是程序的一个实例 一个程序可以对应一个或多个进程。 2.为什么需要进程? 3.进程的组成部分: 进程 pcb 块 栈|堆|bss|data|text 其中: 家族…

车载测试与分析的利器之CANoe

连接汽车神经网络的专家 CANoe不仅是一款强大的总线分析工具,它还集成了仿真、标定和诊断功能,为汽车工程师提供了全方位的车载网络解决方案。从基础的信号监测到复杂的网络交互测试,CANoe凭借其灵活性和高效性,成为了车载测试工程…

浏览器后门:保护影子 IT 的新前线

浏览器扩展是网络犯罪分子的主要目标。这不仅仅是消费者的问题,也是企业对抗影子 IT 的新领域。 最终,扩展权限越多,攻击面就越大。研究表明,平均每个企业的生态系统中都有近 1500 个浏览器扩展程序 - 即使是一个不良插件也可能导…