【C语言】预处理详解(下)

devtools/2024/9/19 11:43:16/ 标签: c语言, chrome, 开发语言, 预处理, java

文章目录

  • 前言
  • 6. 宏和函数的对比
  • 7. #和##
    • 7.1 #运算符
    • 7.2 ##运算符(运用较少,了解即可)
  • 8. 命名的约定
  • 9. #undef (了解即可)
  • 10. 条件编译(重点)
  • 11. 头文件的包含
    • 11.1 头文件被包含的方式:
      • 11.1.1 本地文件包含
      • 11.1.2 库文件的包含

前言

我们接着详解预处理(上)内容给大家继续讲解预处理的有趣之处。

6. 宏和函数的对比

在详解预处理(上)我讲到定义宏时,如果比较两个数的大小,我们可以这样写一个宏:

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

当然我们也可以用函数来实现:

int MAX(int x,int y)
{return x>y ? x : y;
}

那这两种方法哪个更好呢?这就是我们接下来要讨论的问题了。

针对上述的例子,我更倾向使用宏。

原因有二:

  1. 用于调用函数和从函数返回得到代码可能比实际执行这个小型的计算工作所需要的时间更多(也就是创建函数栈帧需要时间)。所以宏比函数在程序的规模和速度方面更胜一筹
  2. 更为重要的是函数的参数必须要其声明特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏可以适用于整型、长整型、浮点型等可以用>来比较类型,也就是宏参数不需要声明类型,这是宏参数的绝对优势。

和函数相比宏的劣势

  1. 每次使用宏时,一份宏定义的代码插入到程序中。除非宏定义比较短,否则可能会大幅度提高程序的长度。
  2. 宏时无法调试的。因为它是处在预处理阶段的。
  3. 宏由于不需要规定参数类型,也就不够严谨。
  4. 宏可能会导致运算符优先级的问题,导致程序很容易出错。

宏有时候可以做到函数做不到的事情。比如:宏的参数可以出现各种类型,但是函数做不到。

#define MALLOC(num,type)\(type*)malloc(num * sizeof(type))...
//使用
MALLOC(10int);//类型作为参数//预处理器替换之后
(int*)malloc(10*sizeof(int));

7. #和##

7.1 #运算符

#运算符是将宏的一个参数转换为字符串字面量。它仅允许出现在带有参数的宏的替换列表中

#运算符所执行的操作可以理解为“字符串化”。

比如当我们有一个变量int a = 10;的时候,我们想打印出:the value of a is 10
我们就可以写成这样:

#define PRINT(n) printf("the value of "#n" is %d",n)

当我们按照下面的方法调用时,
PRINT(a);//当我们把a替换到宏的体内时,就会出现了#a,而#a就转换为了“a”的一个字符串。

7.2 ##运算符(运用较少,了解即可)

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

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

这里我们就想一想,写一个函数求两个数的较大值的时候,不同的数据类型就得写不同的函数。

比如:

int int_max(int x, int y)
{return x>y?x:y;
}float float_max(float x, float y)
{return x>y?x:y;
}

但是这样写过于繁琐了,现在我们这样写代码试试:

#define GENERIC_MAX(type) \
type type##_max(type x,type y)\
{\return x>y?x:y;\
}

使用宏来定义不同的函数:

GENERIC_MAX(int);
GENERIC_MAX(float);int main()
{//调用函数int m = int_max(2,3);printf("%d\n",m);float fm = float_max(3.5f,4.5f);printf("%f\n",fm);return 0;
}

8. 命名的约定

一般来讲函数和宏的使用语法很相似。所以仅凭借复发本身没有办法帮我们区分二者。

那平时我们的一个习惯是:

把宏名全部大写
函数名不要大写

9. #undef (了解即可)

这条语句是用来移除一个宏定义。

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

10. 条件编译(重点)

我们先来聊一聊为什么需要条件编译?

在编译一个程序的时候我们如果要将一条指令(一组指令)编译或者放弃是很方便的。因为我们有条件编译。

比如说:

调试性代码,删除了可惜,保留又碍事,所以我们可以选择性的编译

#include<stdio.h>
#define __DEBUG__int main()
{int i = 0;int arr[10] = {0};for(i = 0; i< 10;i++){arr[i] = i;#ifdef __DEBUG__printf("%d\n",arr[i]);#endif //__DEBUG__}return 0;
}

常见的条件编译的指令:

1.
#if 常量表达式//...
#endif
如:
#define __DEBUG__ 1
#if __DEBUG__//...
#endif2.多个分支的条件编译
#if 常量表达式//...
#elif 常量表达式//...
#else//...
#endif3.判断是否被定义
#if defined(symbol)
#ifdef symbol //上面的简化#if !define(symbol)
#ifndef symbol4.嵌套条件编译指令
#if define(OS_UNIX)#ifdef OPTION1unix_version_option1();#endif#ifdef OPTION2unix_version_option2();#endif
#elif defined(OS_MSDOS)#ifdef OPTION2msdos_version_option2();#endif
#ednif

11. 头文件的包含

你是否还在问为什么得用“”来括起来自己写的头文件名,而不是像stdio.h那样的头文件用<>吗,本小节就来带大家解开谜语。

11.1 头文件被包含的方式:

11.1.1 本地文件包含

#include "filename.h"

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

Linux环境的标准头文件路径:

/usr/include

Windows环境的标准头文件路径:

C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include
//这是VS2013的默认路径

注意:按照自己安装的路径来查找。

11.1.2 库文件的包含

#include<filename.h>

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

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

答案是可以的,但是不推荐这么做。因为这样做查找的效率就会变低,当然这样也不容易区分包含的是本地文件还是库文件

至此,预处理详解的内容就全部完成了。如果觉得讲的还不错的话,麻烦给偶点个赞吧!!!
哈哈哈


http://www.ppmy.cn/devtools/90951.html

相关文章

深入理解JVM堆内存及参数配置:-Xss, -Xms, -Xmx, -Xmn详解

目录 JVM内存结构概述堆内存详解 堆内存的划分新生代与老年代JVM参数概述参数详解 -Xss参数-Xms参数-Xmx参数-Xmn参数实际配置案例<

SpringBoot高性能清理MySQL历史数据

背景 项目的心跳数据&#xff0c;随着业务的 增长及时间的推移&#xff0c;数据库的数据量越来越庞大&#xff0c;不仅查询性能受到了影响&#xff0c;还消耗过多的数据库空间。为了保证查询的性能&#xff0c;需要控制数据量的大小&#xff0c;与业务商讨&#xff0c;仅保…

【深度学习】TTS,CosyVoice,推理部署的代码原理讲解分享

文章目录 demo代码加载配置文件speech_tokenizer_v1.onnx(只在zero_shot的时候使用)campplus.onnx(只为了提取说话人音色embedding)`campplus_model` 的作用代码解析具体过程解析总结示意图CosyVoiceFrontEndCosyVoiceModel推理过程总体推理过程推理速度很慢: https://git…

python爬虫之crawlspider爬取全站数据

python爬虫之crawlspider爬取全站数据 CrawlSpider&#xff1a;类&#xff0c;Spider的一个子类 全站数据爬取的方式&#xff1a; 1、基于Spider&#xff1a;手动请求 2、基于CrawlSpider CrawlSpider的使用&#xff1a; 1、创建一个工程 2、cd xxx 3、创建爬虫文件 scrapy ge…

VSCode学习记录

一、下载相关包 npm install npm install vue-router //路由 npm install axios npm install element-plus --save //组件 二、构建一个简单的项目 1.创建router文件夹&#xff0c;在里面创建一个index.js文件用来管理不同页面的路由 import {createRouter,createWebHashHist…

FPGA面试问题整理

1. 逻辑设计中竞争与冒险概念&#xff0c;如何识别和消除&#xff1f; 竞争&#xff1a;在组合逻辑电路中&#xff0c;信号经过多条路径到达输出端&#xff0c;每条路径经过的逻辑门不同存在时差&#xff0c;在信号变化的瞬间存在先后顺序。这种现象叫竞争。 冒险&#xff1a;由…

谷粒商城实战笔记-117-全文检索-ElasticSearch-进阶-term查询

文章目录 term 查询term最佳实践 match_phrase 查询keyword 查询区别 在Elasticsearch中&#xff0c; term查询是一种基本的查询类型&#xff0c;用于执行不分析&#xff08;不经过分析器处理&#xff09;的精确匹配。以下是 term查询以及与 match_phrase和 keyword查询的比…

vue2 使用axios 请求后台返回文件流导出为excel

目录 步骤 1: 安装 Axios 步骤 2: 创建 Axios 实例 步骤 3: 发起请求并处理文件流 说明 步骤 1: 安装 Axios 首先&#xff0c;确保项目中已经安装了 Axios。如果没有&#xff0c;可以通过以下命令进行安装&#xff1a; npm install axios 步骤 2: 创建 Axios 实例 为了更…

Qt入门(二):第一个Qt项目

新建项目 打开Qt Creator&#xff0c;新建项目&#xff0c;然后一路next 到这一步baseclass有三种选择&#xff1a; QMainWindow&#xff1a;主窗口基类&#xff0c;相较于QWidget&#xff0c;多了菜单栏等杂七杂八的东西。QWidget&#xff1a;最基础的窗口基类&#xff0…

JavaEE: wait(等待) / notify (通知)

文章目录 wait(等待) / notify (通知)总结 wait(等待) / notify (通知) 线程在操作系统上的调度是随机的~ 那么我们想要控制线程之间执行某个逻辑的先后顺序,那该咋办呢? 可以让后执行的逻辑,使用wait, 先执行的线程,在完成某些逻辑之后,通过notify来唤醒对应的wait. 另外,通…

充电不再难,高质量充电体系‘智’领绿色出行新时代

充电不再难&#xff0c;高质量充电体系‘智’领绿色出行新时代 国家发展改革委新闻发言人近日在新闻发布会上郑重声明&#xff0c;将持续强化统筹协调&#xff0c;协同各相关部门加速构建高质量充电基础设施体系&#xff0c;以更有效地满足人民群众对绿色出行的需求。 新能源汽…

利用自然语言处理(NLP)技术挖掘旅游评论数据

目录 简单了解 延伸 如何使用自然语言处理技术提高旅游评论情感倾向的准确性&#xff1f; 旅游评论数据中多模态信息融合的最佳实践是什么&#xff1f; 在旅游评论数据预处理和清洗过程中&#xff0c;哪些方法最有效&#xff1f; 使用Python网络爬虫技术进行旅游评论数据的…

免费【2024】springboot 甘肃“印象”网站的设计与实现

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

Outlook Pst文件大小最大多大?如何分开缩减?

簡介 預設情況下&#xff0c;personal Folders (.pst) 和離線 Outlook Data File (.ost) 檔案在 Microsoft Outlook 2010 和 Outlook 2013 中為 Unicode 格式。 .pst 和 .ost 檔案的整體大小有 50 GB 的預先設定限制。 此限制大於 2007 和 Outlook 2003 Outlook Unicode .pst …

在 Qt 中实现 GIF 动画显示

在 Qt 图形界面编程中&#xff0c;有时我们需要在应用窗口中显示 GIF 动画。要实现这一点&#xff0c;我们可以利用 QGraphicsView 和 QGraphicsScene 类来显示普通图片&#xff0c;同时也可以轻松地扩展以显示 GIF 动画。本文将介绍如何在 Qt 中实现这一功能。 问题描述 一位…

Unity补完计划 之 动态控制TileMap

本文仅作笔记学习和分享&#xff0c;不用做任何商业用途 本文包括但不限于unity官方手册&#xff0c;unity唐老狮等教程知识&#xff0c;如有不足还请斧正 1.TileMap &TileBase Unity - Scripting API: Tilemap &#xff0c;看手册内容太多了故介绍几个常用的公共方法 首…

分享一个基于微信小程序的乡村医疗上门服务预约平台(源码、调试、LW、开题、PPT)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人 八年开发经验&#xff0c;擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等&#xff0c;大家有这一块的问题可以一起交流&…

代码随想录八股训练营day30天

代码随想录八股训练营day30天 1、Java 创建线程有哪几种方式&#xff1f; 在 Java 中&#xff0c;创建线程有四种方式&#xff0c;分别是 继承Thread类&#xff0c;实现Runnable接口&#xff0c; 使用Callable和Future, 使用线程池. &#xff08;1&#xff09;继承Thread类&a…

WPF InkCanvas书写卡顿问题分析

背景 公司有一款授课软件微讲师授课软件&#xff0c;可以在我们的软件上自动书写&#xff0c;勾画&#xff0c;采用InkCanvas做为书写层&#xff0c;并且支持提笔书写功能&#xff0c;为了给用户极致的体验&#xff0c;需要用户在书写时非常流畅。 问题 在使用过程中发现一个…

【C++】C++11中的包装器和绑定器

目录 一、function包装器 1.1 可调用对象 1.2 概念 1.3 应用场景 二、bind绑定器 一、function包装器 1.1 可调用对象 我们平时使用的普通函数、函数指针、仿函数和Lambda表达式都是可调用对象&#xff0c;它们不仅可以作为其他函数的参数传入&#xff0c;还可以作为其他…