当前位置: 首页 > news >正文

C语言-预处理详解

1.预处理符号

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

__FILE__//代表当前进行编译的源文件
__LINE__//文件当前行号
__DATE__//文件当前日期
__TIME__//文件当前时间
__STDC__//如果编译器遵循ANSIC,其值为1,否则未定义

接下来,我们可以试验一下:

#include<stdio.h>int main()
{printf("%s\n",__FILE__);printf("%s\n",__DATE__);printf("%s\n",__TIME__);printf("%d\n",__LINE__);return 0;   
}

经过预处理之后,代码变成了:

int main()
{printf("%s\n","test.c");//目前在编译的文件名printf("%s\n","July 19 2024");printf("%s\n","18:17:54");printf("%d\n",8);return 0;
}

2.#define定义字符常量

基本语法:

                #define 名字 内容

即 #define name stuff

例子:

#define MAX 1000
#define reg register//为register这个关键字创了一个简短的名字
#define do_forever for(;;)//变成死循环了
#define CASE break;case//在写case语句时自动把break写上
//如果定义的语句过长,可以分成几行写,除最后一行外,每行最后都要加上一个反斜杠'\'(续行符)。注意:其后不能有空格,应直接回车,否则,续的就不是原来的语句了。
#define DEBUG_PRINT printf("file:%s\tline:%d\t\date:%s\ttime:%s\n",\__FILE__,__LINE__,    \__DATE__,__TIME__)

            

思考:

在define定义标识符的时候,如果在末尾加上‘;’怎样?

比如:

#define MAX 1000
#define MAX 1000;

建议不要加‘;’,因为可能会导致一些问题。

例:

当用了:

printf("%d\n",MAX);

就会出问题。

如果是这个:

if(condition)max=MAX;
elsemax=0;

那么等替换之后,if和else之间就是两条语句,而没有大括号时if和else后面只能接一条语句,else就不知道是和哪个else匹配的,就会出现语法错误。

3.#define定义宏

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

下面是其

定义方式:

#define name(parament-list) stuff

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

注意:

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

举例:

#define f(x) x*x

4.带有副作用的宏参数

所谓的副作用就是在实现预想的结果的同时,影响了其他的结果。

当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预料的后果。

例如:

#define MAX(a,b) ((a)>(b)?(a):(b))
.....
int x=5;
int y=8;
int z=MAX(x++,y++);

5.宏替换的规则

在程序中扩展#define定义符号和宏时,需要涉及几个步骤。

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

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

3.最后,再对结果文本进行扫描,看看它是否包含任何由#define定义的符号。如果是,则重复上述处理过程。

注意:

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

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

例:

#define M 10

但是“MARY"中的M是不会被替换的。

6.宏与函数的对比

宏通常被应用于执行简单的运算。

比如在两个数中找到较大数。

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

而如果用函数来完成,有一下缺点:

1.用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间要多。所以在小型计算中,宏比函数在程序的规模和速度上要更胜一筹。

2.更为重要的是,函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用,反之,宏可以适用于整型,长整型,浮点型等可以用>来比较大小的类型。宏的参数是类型无关的。

宏有些时候可以做到函数做不到的事。比如:宏的参数可以出现类型,而函数不可以:

#define MALLOC(num,type) (type*)malloc(num*sizeof(type))

和函数相比,宏的劣势:

1.每次使用宏时,一份宏定义的代码将插入到程序中。除非这段代码较短,否则可能大幅度增加程序的长度。

2.宏没法调试

3.宏由于是类型无关的,不够严谨。

4.宏可能带来运算符优先级的问题,导致程序容易出错。

至于第四点,有个例子:

#define MAX(a,b) a*b
//当a=3+1 b=1+2时

7.#和##

一.#

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

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

适用场景:

当我们想要打印出“The value of x is %d"的情况

例如:

The value of b is 12
The value of a is 1

而以上的语句大致相同,需要改变的是某个数据的名字以及数据的具体大小。

为了方便后面的讲解,我们需要补充一个知识点:

在c语言中,printf("Hello world!")与printf("Hello""world!”)的结果是一样的。

回到原来的问题:我们把这个语句定义为宏

即:

#define PRINT(n) printf("The value of"#x"is%d\n",x);

当我们使用这个宏时:

int a=1;
PRINTF(a);

就会变成:

The value of a is 1

而#x就是将x转化为"x"这个字符串。

如果还想在字符串中加一些内容,可以直接#内容。

注意:我们需要使用这个符号的一个原因是:宏在替换时,不会替换字符串内的内容。

二.##

'##'字符用在宏定义里可以把两个符号粘连在一起,合成一个符号。被称为记号粘合。

这样的连接产生的标识符必须是合法的,否则就是未定义的。

它有如下应用场景:

当我们想要找出两个数中的较小数时,不同的数据类型得写不同的函数,但是,这样的函数结构是类似的,于是,我们就想到用宏,然而,这些不同函数的名称也要随着参数改变,同时要是合法的,而宏的参数只有一个,那就是类型,所以,我们想到用记号粘合。

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 TYPE_MAX(type)    \
type type##_max(type x,type y)\
{                            \return (x>y?x:y);        \
}

具体使用:

TYPE_MAX(int)
//在预编译中替换为:
//int_max(int x,int y)
//{
//   return x>y?x:y;
}
TYPE_MAX(float)
//在预编译中替换为:
//float float_max(float x,float y)
//{
//    return x>y?x:y;
//}int main()
{int m=int_max(2,3);float n=float_max(3.5f,4.5f);return 0;
}

8.命名约定

由于宏与函数的使用语法相似,所以语言上无法帮我们区分二者,所以我们通常用一下命名习惯区分:

1.宏的全名全部大写。

2.函数名不要全部大写。

当然,这个规定不一定要遵守,c语言本身就有一些没有遵守,例如:

offsetof———宏:计算结构体成员相较于结构体起始位置的偏移量。

9.#undef

这个指令用于移除一个宏定义。是可以直接写入主函数的,不过需要注意的是:它与#define相同,不用单独加';'来表示语句的结束。

例如:

#define NAME 10int main()
{#undef NAMEreturn 0;
}

10.命令行定义

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

例如:当我们根据同一个源文件要编译出一个程序的不同版本时,这个能力就能起到作用。

假如某个程序中声明了一个某长度的数组,如果机器的内存有限,我们需要一个很小的数组,但是另一个机器的内存大些,我们需要一个更大的数组时,就可以利用这个能力。

好处:代码可以根据需求快速调整。

11.条件编译

利用条件编译指令我们可以很方便的编译或放弃一条或一组语句。

例如:某些语句起到的是调试作用,我们希望在某些条件下使用。

#define _DEBUG_//当不想要ifdef后面的语句时,就注释掉这句int main()
{#ifdef _DEBUG_printf("6\n");#endifreturn 0;
}

常见的条件编译指令:

#if 常量表达式 //如果为假,后面的语句就不参与编译//...
#endif
//注意:一定要以#endif结尾//多分支条件编译#if 常量表达式//...
#elif 常量表达式//...
#else//...
#endif//判断是否被定义
#if defined(symbol)
#ifdef symbol#if !definede(symbol)
#ifndef symbol

注意:这些指令是可以相互嵌套的

12.头文件的包含

一.本地文件的包含:

#include"filename"

查找方式:先在源文件下的目录里找,如果该文件未被找到,编译器就会像找库函数一样在标准位置查找头文件。

如果找不到就显示编译错误。

二.库文件包含

#include<filename>

查找方式:直接在标准文件下查找,如果找不到就提示编译错误。

当然,库文件也可以用""包含,不过,这样的话效率就会低一些,也会没那么容易区分本地文件和库文件。

三.嵌套文件的包含

如果一个同文件被多次包含,编译器在预处理时就会多次替换头文件的内容,这样的重复包含对编译的压力很大。所以,我们可以使用一些方法避免这样的情况出现。

利用条件编译防止重复包含。

方法一:

在每个头文件里加上:

#ifndef __TEST_H__
#define __TEST_H__
//头文件中的内容
#endif

方法二:

在每个头文件中加上:

#pragma once

13.其他预处理指令

目前不想写了,可参考《C语言深度剖析》这本书。

相关文章:

C语言-预处理详解

1.预处理符号 C语言中设置了一些预定义符号&#xff0c;可以直接使用&#xff0c;预定义符号是在预处理期间处理的。 __FILE__//代表当前进行编译的源文件 __LINE__//文件当前行号 __DATE__//文件当前日期 __TIME__//文件当前时间 __STDC__//如果编译器遵循ANSIC&#xff0c;…...

计算机网络-VLAN间通信(三层通信)模拟实现

目录 VLAN基础知识VLAN和普通LAN区别划分VLAN的原因 实现VLAN间的通信&#xff08;三层通信&#xff09;方案一&#xff1a;多臂路由方案二&#xff1a;单臂路由方案三&#xff1a;三层交换机 VLAN基础知识 VLAN&#xff08;Virtual Local Area Network&#xff0c;虚拟局域网…...

【JAVA】数据类型及变量

&#x1f389;欢迎大家收看&#xff0c;请多多支持&#x1f339; &#x1f970;关注小哇&#xff0c;和我一起成长&#x1f680;个人主页&#x1f680; Java的数据类型 可以分为两类&#xff0c;基本数据类型和引用数据类型 基本数据类型有4类8种&#xff0c;4类分别是整型 浮…...

微软蓝屏事件暴露的网络安全问题

目录 1.概述 2.软件更新流程中的风险管理和质量控制机制 2.1.测试流程 2.2.风险管理策略 2.3.质量控制措施 2.4.小结 3.预防类似大规模故障的最佳方案或应急响应对策 3.1. 设计冗余系统 3.2. 实施灾难恢复计划 3.3. 建立高可用架构 3.4. 类似规模的紧急故障下的响应…...

11 - FFmpeg - 编码 AAC

Planar 模式是 ffmpeg内部存储模式&#xff0c;我们实际使用的音频文件都是Packed模式的。 FFmpeq解码不同格式的音频输出的音频采样格式不是一样。 其中AAC解码输出的数据为浮点型的 AV_SAMPLE_FMT_FLTP 格式&#xff0c;MP3 解码输出的数据为 AV_SAMPLE_FMT_S16P 格式(使用的…...

OS Copilot初体验的感受与心得

本文介绍体验操作系统智能助手OS Copilot后&#xff0c;个人的一些收获、体验等。 最近&#xff0c;抽空体验了阿里云的操作系统智能助手OS Copilot&#xff0c;在这里记录一下心得与收获。总体观之&#xff0c;从个人角度来说&#xff0c;感觉这个OS Copilot确实抓住了不少开发…...

Ajax学习笔记

文章目录标题 Ajax学习笔记axios使用axios请求拦截器axios响应拦截器优化axios响应结果 form-serialize插件图片上传HTTP协议请求报文相应报文接口文档 AJAX原理 - XMLHttpRequest使用XMLHttpRequestXMLHttpRequest - 查询参数查询字符串对象 XMLHttpRequest - 数据提交 事件循…...

医学深度学习与机器学习融合的随想

医学深度学习与机器学习融合的随想 近年来&#xff0c;深度学习&#xff08;图像类&#xff09;和机器学习在医学领域的应用取得了飞速发展&#xff0c;为医学影像分析、疾病诊断和预后预测等领域带来了革命性的变革。深度学习擅长从复杂数据中提取高层次特征&#xff0c;而机…...

坑人的macos tar 命令 (实际上是bsdtar)换用 gnu tar

周末 看着笔记本上好用的朗文当代高级词典(mac版)和其它两部词典&#xff0c;准备复制到黑苹果台式机上去。考虑到词典内容有太多小文件&#xff0c;普通复制传输太慢&#xff0c;毫无疑问用 tar 打包肯定快而且能保留原始文件的各种信息。命令如下&#xff1a; time tar czf …...

【SpringBoot3】全局异常处理

【SpringBoot3】全局异常处理 一、全局异常处理器step1&#xff1a;创建收入数字的页面step2:创建控制器&#xff0c;计算两个整数相除step3:创建自定义异常处理器step5&#xff1a;创建给用提示的页面step6&#xff1a;测试输入&#xff08;10/0&#xff09; 二、BeanValidato…...

vue-Treeselect

一、Node KeyTypeDescriptionid (required)Number | String用于标识树中的选项。其值在所有选项中必须是唯一的label (required)String用于显示选项childrennode[] | null声明一个分支节点。你可以&#xff1a; 1&#xff09; 设置为由a组成的子选项数组。叶节点&#xff0c;b…...

【机器学习框架TensorFlow和PyTorch】基本使用指南

机器学习框架TensorFlow和PyTorch&#xff1a;基本使用指南 目录 引言TensorFlow概述 TensorFlow简介TensorFlow的基本使用 PyTorch概述 PyTorch简介PyTorch的基本使用 TensorFlow和PyTorch的对比结论 引言 随着深度学习的快速发展&#xff0c;机器学习框架在实际应用中起到…...

matlab 中的methods(Access = protected) 是什么意思

gpt版本 在 MATLAB 中&#xff0c;methods 是用于定义类方法的一部分。(Access protected) 是一种访问控制修饰符&#xff0c;它限制了方法的访问权限。具体来说&#xff0c;当你在类定义中使用 methods(Access protected) 时&#xff0c;你是在定义只有类本身及其子类可以访…...

【漏洞复现】Netgear WN604 downloadFile.php 信息泄露漏洞(CVE-2024-6646)

0x01 产品简介 NETGEAR WN604是一款由NETGEAR&#xff08;网件&#xff09;公司生产的无线接入器&#xff08;或无线路由器&#xff09;提供Wi-Fi保护协议&#xff08;WPA2-PSK, WPA-PSK&#xff09;&#xff0c;以及有线等效加密&#xff08;WEP&#xff09;64位、128位和152…...

图像处理 -- ISP调优(tuning)的步骤整理

ISP调优流程培训文档 1. 硬件准备 选择合适的图像传感器&#xff1a;根据项目需求选择合适的传感器型号。搭建测试环境&#xff1a;包括测试板、光源、色彩卡和分辨率卡等。 2. 初始设置 寄存器配置&#xff1a;初始化传感器的寄存器设置&#xff0c;包括曝光、增益、白平衡…...

【中项】系统集成项目管理工程师-第4章 信息系统架构-4.2系统架构

前言&#xff1a;系统集成项目管理工程师专业&#xff0c;现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试&#xff0c;全称为“全国计算机与软件专业技术资格&#xff08;水平&#xff09;考试”&…...

node.js中nodemon : 无法加载和使用问题,这是由于windows安全策略影起的按如下操作即可

1、用管理员权限打开vscode 2、文件终端中打开&#xff0c;输入 Set-ExecutionPolicy -Scope CurrentUser 3、再输入RemoteSigned 4、使用get-ExecutionPolicy查看权限&#xff0c;可以看到变为了RemoteSigned 重启问题解决...

【SD】 Stable Diffusion(SD)原理详解与ComfyUI使用 2

Stable Diffusion&#xff08;SD&#xff09;原理详解与ComfyUI使用 Stable Diffusion&#xff08;SD&#xff09;原理详解与ComfyUI使用1. SD整体结构2. Clip&#xff08;文本编码器&#xff09;3. Unit&#xff08;生成模型&#xff09;4. VAE&#xff08;变分自编码器&#…...

【学习笔记】无人机系统(UAS)的连接、识别和跟踪(七)-广播远程识别码(Broadcast Remote ID)

目录 引言 5.5 广播远程识别码&#xff08;Broadcast Remote ID&#xff09; 5.5.1 使用PC5的广播远程识别码 5.5.2 使用MBS的广播远程识别码 引言 3GPP TS 23.256 技术规范&#xff0c;主要定义了3GPP系统对无人机&#xff08;UAV&#xff09;的连接性、身份识别、跟踪及…...

VMware 虚拟机 ping 不通原因排查

目录 一、检查网络 二、重启虚拟机网络 因为最近遇到了一个比较奇怪的 ping 不通虚拟机的事&#xff0c;在此过程中&#xff0c;检查了很多的设置&#xff0c;故而写一篇文章记录下&#xff0c;如有 VMware 虚拟机 ping 不通可以尝试本文的排查方式。 下面以 VMware 虚拟机为…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

以光量子为例,详解量子获取方式

光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学&#xff08;silicon photonics&#xff09;的光波导&#xff08;optical waveguide&#xff09;芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中&#xff0c;光既是波又是粒子。光子本…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...