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

news2025/1/23 13:03:57

文章目录

  • 前言
  • 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__
	//...
#endif

2.多个分支的条件编译
#if 常量表达式
	//...
#elif 常量表达式
	//...
#else
	//...
#endif

3.判断是否被定义
#if defined(symbol)
#ifdef symbol //上面的简化

#if !define(symbol)
#ifndef symbol

4.嵌套条件编译指令
#if define(OS_UNIX)
		#ifdef OPTION1
			unix_version_option1();
		#endif
		#ifdef OPTION2
			unix_version_option2();
		#endif
#elif defined(OS_MSDOS)
		#ifdef OPTION2
			msdos_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.coloradmin.cn/o/1988273.html

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

相关文章

<数据集>柑橘缺陷识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;1290张 标注数量(xml文件个数)&#xff1a;1290 标注数量(txt文件个数)&#xff1a;1290 标注类别数&#xff1a;4 标注类别名称&#xff1a;[Orange-Green-Black-Spot, Orange-Black-Spot, Orange-Canker, Orange…

CTFHUB | web进阶 | PHP | Bypass disable_function | Apache Mod CGI

开启题目 点击重置 backdoor 目录&#xff0c;再点击 GetFlag&#xff0c;然后发现了可以蚁剑连接&#xff0c; 连接成功发现无任何发现&#xff0c;所以我们使用 apache_mode_cgi插件 发现直接进入终端了&#xff0c;最后发现了 flag

深入解析Python内省之dir、getattr、hasattr和setattr使用详解

概要 内省(Introspection)是编程语言的一种能力,使程序能够在运行时检查对象的类型、属性和方法。Python提供了强大的内省工具,允许开发者动态地检查和操作对象的属性和方法。本文将详细介绍Python中的内省工具:dir、getattr、hasattr和setattr,并通过具体的示例代码展示…

springboot 股票资产管理系统-计算机毕业设计源码96208

摘要 随着全球金融市场的快速发展&#xff0c;股票交易和投资已经成为重要的经济活动之一。在此背景下&#xff0c;股票资产管理系统的设计与实现显得尤为重要。Spring Boot框架&#xff0c;以其快速、简洁和高效的特性&#xff0c;在股票资产管理系统的开发中得到了广泛应用。…

C++第三十二弹---从概念到实践:全面解析C++多态性

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1. 多态的概念 1.1 概念 2. 多态的定义及实现 2.1 多态的构成条件 2.2 虚函数 2.3 虚函数的重写 2.4 C11 override 和 final 2.5 重载、覆…

【Linux】安装部署docker及docker-compose

环境说明 操作系统&#xff1a;Ubuntu 22.04 架构&#xff1a;x86_64 一、docker安装 1.下载docker源码包 下载地址&#xff1a;https://download.docker.com/linux/static/stable/x86_64/ 注意&#xff1a;如果这个地址互联网打不开&#xff0c;那就开代理访问&#xff…

基于STM32的智能家居安全系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 初始化代码安全传感器读取代码应用场景 家居安全监控办公环境安全监控常见问题及解决方案 常见问题解决方案结论 1. 引言 在智能家居和办公环境中&#xff0c;安全系统是一个至关重要的组成…

这个低代码开发丝滑小连招,竟还能本地部署?

在这个飞速发展的数字化时代&#xff0c;我们深知每一位开发者对于高效、灵活、安全的开发环境有着不懈的追求。为此&#xff0c;JNPF低代码开发平台最新推出的V5.0版本现已全面支持用户免费本地部署体验&#xff01;这一重大福利&#xff0c;将为您带来前所未有的开发体验&…

leetcode数论(2280. 表示一个折线图的最少线段数)-几何

前言 经过前期的基础训练以及部分实战练习&#xff0c;粗略掌握了各种题型的解题思路。现阶段开始专项练习。 数论包含最大公约数(>2个数)、最大公约数性质、最小公倍数、区间范围质因素计数(最下间隔)、质因素分解、判断质数、平方根、立方根、互质、同余等等。 描述 给…

SwiftUI 如何定制 Picker 视图当前选中行的背景颜色?

功能需求 有时我们希望可以定制 SwiftUI 中 Picker 视图当前选中行的背景色,这可以做到吗? 在上面的演示图中,我们随心所欲地变换着 SwiftUI 中 Picker 视图当前选中行的背景色。这是怎么做到的呢? 在本篇博文中,您将学到以下内容 功能需求1. 钩深极奥:修改 SwiftUI 原…

为什么总是很难很好的理解电工电子知识?终于有了解决的办法!

理解电工电子知识可能具有挑战性&#xff0c;这可以由多种因素造成。以下是一些可能的原因&#xff1a; 抽象性高&#xff1a;电工电子学涉及到电流、电压、电阻、电容、电感等抽象概念&#xff0c;这些概念在日常生活中不易直接观察和体验&#xff0c;因此需要较高的抽象思维…

【nginx】centos7安装并配置开机自启

【nginx】配置开机自启 1.nginx配置开机自启 安装完成nginx之后 vim /lib/systemd/system/nginx.service[Unit] Descriptionnginx Afternetwork.target[Service] Typeforking ExecStart/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf ExecReload/usr/loc…

使用开源 LLM 充当 LangChain 智能体

太长不看版 开源 LLM 现已达到一定的性能水平&#xff0c;可堪作为智能体工作流的推理引擎。在我们的测试基准上&#xff0c;Mixtral 甚至已超越 GPT-3.5&#xff0c;而且我们还可以通过微调轻松地进一步提高其性能。 引言 经由因果语言建模任务训练出的大语言模型&#xff…

鸿蒙应用服务开发【华为支付服务】客户端

华为支付服务 介绍 本示例展示了使用华为支付服务提供的单次支付&#xff0c;签约代扣。 需要使用华为支付服务接口 kit.PaymentKit。 效果预览 Sample工程的配置与使用 在DevEco中配置Sample工程的步骤如下 [创建项目]及[应用]。打开Sample应用&#xff0c;使用[AppGall…

【JavaEE精炼宝库】网络原理基础——UDP详解

文章目录 一、应用层二、传输层2.1 端口号&#xff1a;2.2 UDP 协议&#xff1a;2.2.1 UDP 协议端格式&#xff1a;2.2.2 UDP 存在的问题&#xff1a; 2.3 UDP 特点&#xff1a;2.4 基于 UDP 的应用层协议&#xff1a; 一、应用层 我们 Java 程序员在日常开发中&#xff0c;最…

2024年大模型LLM还有哪些可研究细分领域?

Pretraining部分 Data Collection 整个pretrain阶段最重要的部分就是数据收集&#xff0c;尽管OpenAI已经给我们了一套标准化的数据收集流程并也有很多开源机构给予了预训练数据&#xff08;例如common crawl [1]&#xff0c;starcoder等网络数据 [2]&#xff09;&#xff0c…

清华大学终于把Python整理成了《漫画书》

前言 随着人工智能的发展&#xff0c;Python近两年也是大火&#xff0c;越来越多的人加入到Python学习大军&#xff0c;对于毫无基础的人该如何入门Python呢&#xff1f;小编这里整理了一套python编程零基础自学教程&#xff0c;清华大佬196小时讲完的&#xff0c;全程干货无废…

如何将avi格式转换为flv格式呢?

FLV是随着FLASH MX的推出发展而来的一种视频格式&#xff0c;目前被众多新一代视频分享网站所采用&#xff0c;是目前增长较快&#xff0c;也较为广泛的视频传播格式。 FLV格式可以轻松导入FLASH播放器中&#xff0c;另外它还能起到保护版权的作用&#xff0c;非常受欢迎。那么…

uniapp实现可视化图表(轻量、内存小)

图表官网&#xff1a;uCharts官网 - 秋云uCharts跨平台图表库 用原生组件&#xff1a; 选择自己需要的模块&#xff0c;以小程序为例&#xff1a; 把min.js下载下来 把min.js放到小程序代码中&#xff0c;引用即可&#xff0c;使用案例看官网&#xff0c; 在官网中选择想要的…

Embedding技术之Graph Embedding

Graph Embedding用于处理互联网中的图数据——社交网络、知识图谱、行为关系类型图数据。 1、DeepWalk——基于随机游走的Graph Embedding DeepWalk 是一种用于学习图&#xff08;网络&#xff09;中节点的低维向量表示&#xff08;即节点嵌入&#xff09;的算法。 DeepWalk …