C语言之预处理
目录
前言
宏定义define的用法
文件包含include的用法
条件编译的用法
其他预处理命令
练习题
练习一
练习二
练习三
前言
预处理命令可以改变程序设计环境,提高编程效率,它们并不是C语言本身的组成部分,不能直接对它们进行编译,必须在对程序进行编译之前,先对程序中这些特殊的命令进行“预处理”。经过预处理后,程序就不再包括预处理命令了,最后再由编译程序对预处理之后的源程序进行编译处理,得到可供执行的目标代码。C语言提供的预处理功能有三种,分别为宏定义、文件包含和条件编译。
宏定义define的用法
在C语言中,#define是用于创建宏定义的预处理指令。宏定义是一种简单的文本替换机制,可以用来定义常量、函数或代码片段。它的基本语法如下:
#define 宏名 替换文本
其中,宏名是你给宏定义起的名称,替换文本是要替换的内容。
下面是一些常见的宏定义用法示例:
1、定义常量:
#define PI 3.14159
这里将宏名PI定义为常量值3.14159,后续使用PI时会被替换为3.14159。
2、定义带参数的宏:
#define SQUARE(x) ((x) * (x))
这里定义了一个带参数的宏SQUARE,用于计算一个数的平方。当使用SQUARE(5)时,宏展开后会变成((5) * (5)),即25。
3、定义条件编译:
#define DEBUG
这里定义了一个宏DEBUG,用于开启调试模式。在代码中可以使用#ifdef和#endif来根据宏是否定义执行不同的代码块。
4、定义复杂的宏:
#define MAX(a, b) ((a) > (b) ? (a) : (b))
这里定义了一个宏MAX,用于返回两个数中的最大值。当使用MAX(10, 20)时,宏展开后会变成((10) > (20) ? (10) : (20)),即20。
需要注意的是,宏定义只是简单的文本替换,没有类型检查和作用域限制。因此,在使用宏定义时需要谨慎,避免出现意外的结果。
另外,为了增加代码的可读性和可维护性,通常建议在宏定义时加上括号,以避免优先级问题。例如,在上面的示例中,将宏定义改为#define SQUARE(x) ((x) * (x)),可以确保在宏展开时正确计算。
总结起来,#define是C语言中用于创建宏定义的预处理指令,可以用来定义常量、函数或代码片段。宏定义通过简单的文本替换机制,在编译前将宏名替换为对应的文本。合理使用宏定义可以提高代码的可读性和灵活性,但也需要注意避免潜在的问题。
文件包含include的用法
在C语言中,#include是用于包含头文件的预处理指令。头文件包含了函数声明、宏定义和类型定义等信息,可以被多个源文件共享使用。#include指令告诉编译器在编译时将指定的头文件内容插入到当前文件中。它的基本语法如下:
#include <头文件名>
或者
#include "头文件名"
其中,<头文件名>用于包含系统提供的标准头文件,而"头文件名"用于包含用户自定义的头文件。
下面是一些常见的#include用法示例:
1、包含标准库头文件:
#include <stdio.h>
这里使用#include <stdio.h>包含了标准库头文件stdio.h,该头文件中包含了输入输出相关的函数声明和常量定义。
2、包含自定义头文件:
#include "myheader.h"
这里使用#include "myheader.h"包含了自定义的头文件myheader.h,该头文件中可以包含用户自定义的函数声明、宏定义和类型定义等。
需要注意的是,头文件通常包含在.h文件中,并且应该包含相应的函数声明和宏定义,而不是实际的函数实现。函数实现通常在对应的.c文件中。
另外,为了避免重复包含同一个头文件,可以使用条件编译指令#ifndef、#define和#endif来保护头文件内容。例如:
#ifndef MYHEADER_H
#define MYHEADER_H// 头文件内容...#endif
这样可以确保头文件只被包含一次,避免重复定义和编译错误。
总结起来,#include是C语言中用于包含头文件的预处理指令,可以将头文件的内容插入到当前文件中。通过包含头文件,可以共享函数声明、宏定义和类型定义等信息,提高代码的重用性和可维护性。在使用#include时,需要注意选择正确的头文件,并遵循良好的编码规范。
条件编译的用法
在C语言中,条件编译是一种预处理指令,用于根据条件来选择性地编译代码块。条件编译可以根据宏的定义与否,决定是否编译某段代码。这对于在不同平台、不同编译选项下编写可移植的代码非常有用。
条件编译使用以下几个预处理指令:
1、#ifdef 和 #endif:
#ifdef 宏名// 被包含的代码块
#endif
如果宏宏名已经定义,则会编译#ifdef和#endif之间的代码块,否则会忽略这部分代码。
2、#ifndef 和 #endif:
#ifndef 宏名// 被包含的代码块
#endif
如果宏宏名没有定义,则会编译#ifndef和#endif之间的代码块,否则会忽略这部分代码。
3、#if、#elif、#else 和 #endif:
#if 表达式// 被包含的代码块
#elif 表达式// 被包含的代码块
#else// 被包含的代码块
#endif
根据表达式的真假情况,选择性地编译相应的代码块。只有满足条件的代码块会被编译,其他代码块会被忽略。
这些条件编译指令可以与宏定义结合使用,例如:
#define DEBUG#ifdef DEBUG// 调试模式下的代码
#else// 发布模式下的代码
#endif
在上述示例中,如果宏DEBUG已经定义,则会编译调试模式下的代码,否则会编译发布模式下的代码。
需要注意的是,条件编译是在预处理阶段进行的,与运行时逻辑无关。因此,条件编译指令不会影响程序运行时的行为。
另外,条件编译还可以用于根据不同平台或编译选项来选择性地包含不同的头文件。例如:
#ifdef _WIN32#include <windows.h>
#elif __linux__#include <unistd.h>
#endif
在上述示例中,根据不同的操作系统平台,选择性地包含了不同的头文件。
总结起来,条件编译是C语言中一种用于根据条件选择性编译代码块的预处理机制。通过使用#ifdef、#ifndef、#if、#elif、#else和#endif等条件编译指令,可以根据宏的定义与否、表达式的真假等情况来选择性地编译代码,提高代码的可移植性和灵活性。在使用条件编译时,需要注意合理选择条件,并遵循良好的编码规范。
其他预处理命令
除了上面介绍的之外,C语言还有#error、#line、#pragma等其他常用的预处理命令,在很多C语言的程序中也是经常可见的,下面简单介绍一下它们。
1、#undef:
#undef 宏名
取消一个已经定义的宏。例如,#undef PI 可以取消之前定义的PI宏。
2、#pragma:
#pragma 指令名
使用指定的编译器指令。不同的编译器支持的指令可能不同,一些常见的指令包括#pragma once(保证头文件只被包含一次)、#pragma pack(n)(设定结构体的字节对齐方式)等。
3、#error:
#error 错误信息
在编译时输出指定的错误信息,并停止编译。例如,#error "Invalid input" 可以输出错误信息Invalid input并停止编译。
4、#warning:
#warning 警告信息
在编译时输出指定的警告信息,但不会停止编译。例如,#warning "Unused variable" 可以输出警告信息Unused variable。
需要注意的是,预处理指令是在编译前由预处理器处理的,它们不是C语言的语句,也不会被编译成机器码。因此,在使用预处理指令时,需要注意遵循预处理器的语法规则,并且遵循良好的编码规范。
总结起来,C语言还提供了一些其他的预处理指令,包括#define、#undef、#include、#pragma、#error和#warning等。通过使用这些指令,可以在编译前对源代码进行预处理,提高代码的可读性、可维护性和可移植性。在使用预处理指令时,需要注意遵循预处理器的语法规则,并遵循良好的编码规范。
练习题
练习一

参考答案
#include <stdio.h>#define SWAP(a, b) do { \int temp = a; \a = b; \b = temp; \
} while(0)int main() {int num1, num2;scanf("%d %d", &num1, &num2);SWAP(num1, num2);printf("%d %d\n", num1, num2);return 0;
}
在上述代码中,我们定义了一个带参数的宏SWAP,它接受两个参数a和b。在宏展开时,我们使用一个临时变量temp来保存a的值,然后将b的值赋给a,最后将temp的值赋给b,实现了两个参数值的互换。
在main函数中,我们首先使用scanf函数从输入中读取两个数num1和num2。然后,我们调用宏SWAP,将num1和num2作为实参传递给宏,实现了两个数值的互换。最后,我们使用printf函数输出交换后的两个数num1和num2。
运行程序,输入示例中的1 2,即可得到输出结果2 1,符合题目要求。
练习二

参考答案
#include <stdio.h>
#include <math.h>#define CALCULATE_S(a, b, c) ((a + b + c) / 2.0)
#define CALCULATE_AREA(a, b, c) (sqrt(CALCULATE_S(a, b, c) * \(CALCULATE_S(a, b, c) - a) * \(CALCULATE_S(a, b, c) - b) * \(CALCULATE_S(a, b, c) - c)))int main() {double a, b, c;scanf("%lf %lf %lf", &a, &b, &c);double area = CALCULATE_AREA(a, b, c);printf("%.3lf\n", area);return 0;
}
在上述代码中,我们定义了两个带参数的宏CALCULATE_S和CALCULATE_AREA,分别用于计算三角形的半周长S和面积area。在宏展开时,我们使用括号将各个表达式括起来,避免运算优先级带来的错误。
在main函数中,我们首先使用scanf函数从输入中读取三角形的三条边a、b、c。然后,我们调用宏CALCULATE_AREA,将a、b、c作为实参传递给宏,计算出三角形的面积。最后,我们使用printf函数输出计算得到的面积area,并保留三位小数。
运行程序,输入示例中的3 4 5,即可得到输出结果6.000,符合题目要求。
练习三

参考答案
#include <stdio.h>#define LEAP_YEAR(y) ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0 ? "L" : "N")int main() {int year;scanf("%d", &year);printf("%s\n", LEAP_YEAR(year));return 0;
}
在上述代码中,我们定义了一个宏LEAP_YEAR,用于判断给定的年份是否为闰年。在宏展开时,我们使用条件运算符(三目运算符)来进行判断:如果年份能被4整除且不能被100整除,或者能被400整除,则返回字符串"L"表示是闰年,否则返回字符串"N"表示不是闰年。
在main函数中,我们首先使用scanf函数从输入中读取年份year。然后,我们调用宏LEAP_YEAR,将year作为实参传递给宏,根据返回的字符串结果输出相应的结果。最后,我们使用printf函数输出结果,并换行。
运行程序,输入示例中的2000,即可得到输出结果L,符合题目要求。如果输入其他年份,程序也能正确判断并输出相应的结果。
相关文章:
C语言之预处理
目录 前言 宏定义define的用法 文件包含include的用法 条件编译的用法 其他预处理命令 练习题 练习一 练习二 练习三 前言 预处理命令可以改变程序设计环境,提高编程效率,它们并不是C语言本身的组成部分,不能直接对它们进行编译&am…...
css步骤条
html 代码以及样式 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>css步骤条</title><style>.steps {display: flex;justify-content: space-between;padding: 0;margin: 20px 10px;lis…...
[Hive] 常见函数
文章目录 字符串函数数值函数随机函数日期和时间函数字符串转时间 聚合函数数组函数结构体函数数组函数映射函数 map正则处理JSON 字符串函数 CONCAT(string1, string2, …):将多个字符串连接成一个字符串。 LENGTH(string):返回字符串的长度。 LOWER…...
Mac用NTFS文件夹读写NTFS硬盘 NTFS能复制多大的文件
Mac作为一款备受欢迎的计算机操作系统,具备了许多令人惊叹的功能和特性。然而,对于一些Mac用户来说,使用NTFS格式的硬盘可能存在一些疑问。他们可能想知道Mac是否能够读写NTFS格式的硬盘,以及NTFS格式的硬盘是否有文件大小的限制。…...
【unity3D】Scroll Rect组件—制作下滑列表
💗 未来的游戏开发程序媛,现在的努力学习菜鸡 💦本专栏是我关于游戏开发的学习笔记 🈶本篇是unity的Scroll Rect组件 Scroll Rect组件 基础知识详细说明案例演示——制作一个简单的下滑框扩展 介绍:Scroll Rect组件是用…...
网络扫描与网络监听
前言:前文给大家介绍了网络安全相关方面的基础知识体系,以及什么是黑客,本篇文章笔者就给大家带来“黑客攻击五部曲”中的网络扫描和网络监听 目录 黑客攻击五部曲 网络扫描 按扫描策略分类 按照扫描方式分类 被动式策略 系统用户扫描 …...
Codeforces Round 904 (Div. 2) C
C. Medium Design 思路:我们设最大值所在的下标为 x x x,最小值所在的下标为 y y y,那么我们考虑一段区间对于答案的贡献: 若一段区间覆盖了 x x x,但没有覆盖 y y y,那么这段区间需要选择 若一段区间覆盖了 y y y,但没有覆盖 x…...
DBeaver连接数据库报错:Public Key Retrieval is not allowed 的解决方案
写在前面: DBeaver是一款免费的数据库管理工具,安装也是傻瓜式一键安装,比较推荐。 DBeaver官网(加载有点慢,耐心等待):DBeaver Community | Free Universal Database Tool 报错详情ÿ…...
DeepFace【部署 04】轻量级人脸识别和面部属性分析框架deepface使用Docker部署CPU+GPU两个版本及cuDNN安装
使用Docker部署CPUGPU 1.CPU2.GPU3.cuDNN安装3.1 Prerequisites3.2 下载Linux版本cuDNN3.3 安装 1.CPU 本说明基于DeepFace的Docker镜像文件deepface_image.tar进行说明。 # 1.导入镜像 docker load -i deepface_image.tar# 2.创建模型文件夹【并将下载好的模型文件上传】 mk…...
程序生活 - 减肥小记
文章目录 缘起健康就好了吗?关于外在和物质生活难与易 我的减肥生活一些细节轻断食戒糖、油炸、重口味睡眠改变社交方式用运动化解压力不喝牛奶 缘起 2017年的一次腿受伤,让我从一个怎么都吃不胖的人,变成了一个实实在在的胖子。 如果你从来…...
深度学习_4_实战_直线最优解
梯度 实战 代码: # %matplotlib inline import random import torch import matplotlib.pyplot as plt # from d21 import torch as d21def synthetic_data(w, b, num_examples):"""生成 Y XW b 噪声。"""X torch.normal(0,…...
《视觉SLAM十四讲》公式推导(三)
文章目录 CH3-8 证明旋转后的四元数虚部为零,实部为罗德里格斯公式结果 CH4 李群与李代数CH4-1 SO(3) 上的指数映射CH4-2 SE(3) 上的指数映射CH4-3 李代数求导对极几何:本质矩阵奇异值分解矩阵内积和迹 CH3-8 证明旋转后的四元数虚部为零,实部…...
pnpm、npm、yarn的区别
pnpm、npm、yarn是三种不同的包管理器,它们之间有一些区别。 安装速度:pnpm的安装速度比npm和yarn快,因为它使用了只下载必需的模块,而不是下载整个依赖树。此外,pnpm还可以并行下载模块,从而进一步提高下…...
搞定蓝牙——第四章(GATT协议)
搞定蓝牙——第四章(GATT协议) 原理介绍层次结构server和client端Attribute ESP32代码 文章下面用的英文表示: server和client:服务端和客户端 char.:characteristic缩写,特征 Attribute:属性 ATT:Attribut…...
Go语言入门心法(十四): Go操作Redis实战
Go语言入门心法(一): 基础语法 Go语言入门心法(二): 结构体 Go语言入门心法(三): 接口 Go语言入门心法(四): 异常体系 Go语言入门心法(五): 函数 Go语言入门心法(六): HTTP面向客户端|服务端编程 Go语言入门心法(七): 并发与通道 Go语言入门心法(八): mysql驱动安装报错o…...
Java学习笔记(三)
前言 这个主要就是想记录一个点,就是二维数组保存的元素就是一维数组的地址,这个概念大家都知道了,那么接下来就是我最近写程序发生的一个事情了。 随机打乱一个一维数组 这个程序我相信大家都是会写的,通过randomArr来随机打乱…...
Flutter笔记:GetX模块中不使用 Get.put 怎么办
Flutter笔记 GetX模块中不使用 Get.put 怎么办 作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263 邮箱 :291148484163.com 本文地址:https://blog.csdn.net/qq_28550263/article/details/13400672…...
2023前端面试整理
1. 介绍一下最近参与的项目,负责那些业务,在开发过程中遇到过问题吗?最后是咋样处理的? 之前负责过大小十几个项目,负责过浙里办的整套上架流程,负责过数据大屏统计,后台管理系统文书生成表单生成等,浙政钉…...
文化融合:TikTok如何弥合跨文化差异
随着全球化的加速和数字媒体的崛起,社交媒体平台已经成为连接世界各地人们的纽带。其中,TikTok作为一个引领者,正在以惊人的速度消除跨文化差异,促进文化融合,使人们更加了解和尊重不同背景和传统。 本文将深入探讨Ti…...
asp.net core获取config和env
配置文件的读取和使用 //读取配置文件直接使用 var configModel configuration.GetSection("DataBaseConfig").Get<DataBaseConfigModel>(); //读取配置文件注入到IOC中 services.Configure<AssemblyConfig>(configuration.GetSection("AssemblyC…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...
【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验
Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...
SQL Server 触发器调用存储过程实现发送 HTTP 请求
文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...
