news 2026/8/31 5:43:04

C语言——预处理器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C语言——预处理器

基本介绍

预处理指令:

预处理过程中会执行预处理指令,预处理指令以 # 号开头,用于指导预处理器执行不同的任务。

预处理指令的特点:

(1)预处理指令应该放在代码的开头部分。
(2)预处理指令都以 # 开头,指令前面可以有空白字符(比如空格或制表符),# 和指令的其余部分之间也可以有空格,但是为了兼容老的编译器,一般不留空格
(3)预处理指令都是一行的,除非在行尾使用反斜杠,将其折行。
(4)预处理指令不需要分号作为结束符,指令结束是通过换行符来识别的。
(5)预处理指令通常不能写在函数内部,有些编译器的扩展允许将预处理指令写在函数里,但强烈不建议这么干。

宏定义#define

宏定义

#define 宏名称 替换文本

宏名称满足标识符规范,建议使用大写

在预处理阶段,所有的宏名称会被替换成对应的替换文本

预处理阶段:宏定义直接文本替换(PI1→3.14),const在运行时创建常量

#include <stdio.h> // 1. 使用宏定义定义常量 #define PI1 3.14 // 2. 使用宏定义给数据类型取别名 #define BOOL int // 3. 替换文本还可以是表达式或者语句 #define EX 200 / 2 + 78 #define DN int n = 100; // 4. 替换文本中还可包含其他宏名称 #define PEX printf("%d\n", EX) //------------------------------------------- //宏定义取消之前是可以使用的 // 取消宏定义 //#undef PI1 //------------------------------------------- // 使用const定义常量 const double PI2 = 3.15; // typedef 数据类型别名 typedef unsigned long long U64; #define 整型 int #define 浮点 double #define 如果 if #define 否则 else #define 打印 printf int main(void) { printf("PI1 = %.2lf\n", PI1); printf("PI2 = %.2lf\n", PI2); BOOL n1 = 1; U64 n2 = 100; printf("n1=%d,n2=%lld\n", n1, n2); int n3 = EX; printf("n3=%d\n",n3); DN printf("%d\n",n); PEX; // 中文编程 ------------------------------ 整型 年龄 = 18; 如果 (年龄 >= 18) { 打印("欢迎光临!"); } 否则 { 打印("请在家长陪同下访问!"); } return 0; }

取消宏定义

#undef 宏名称

带参数的宏定义

#define 宏名称(形参列表) 可以包含形参的替换文本
// 带参数的宏定义 返回两个数中较大的 #define MAX(a,b) a > b ? a : b // 带参数的宏 定义计算平方, 替换文本中的参数一般建议使用小括号括起来 #define SQ1(n) n * n #define SQ2(n) (n) * (n) // 带参宏定义 实现立方计算 #define CUBE(n) (n)*(n)*(n) int main() { printf("较大的数:%d \n", MAX(108, 67)); // 替换为: printf("较大的数:%d \n", 108 > 67 ? 108 : 67); printf("平方: %d \n", SQ1(4+3)); // 替换为: printf("平方: %d \n", 4+3 * 4+3); printf("平方: %d \n", SQ2(4+3)); // 替换为:printf("平方: %d \n", (4+3) * (4+3)); }

文件包含#include

包含标准库头文件

#include <头文件名>

包含自定义头文件

#include "头文件路径"
1. 相对路径 目标文件(头文件)在当前文件(源文件)的同级或者下级,路径以 ./ 开头,./ 可以省略 目标文件(头文件)在当前文件(源文件)上级、更上级,或者需要先去到上级目录,路径以 ../ 开头 ../ 表示上一级目录 ../../ 表示上上极目录 ../../../ 表示上上上级目录 2. 绝对路径 目标文件在计算机上绝对的位置

类 Unix 系统中的路径使用/, windows 系统的路径中使用\/

/*********************************************************** 目录结构 ├─ 03-include-example │ ├─ myheader05.h │ └─ project │ ├─ inc │ │ └─ myheader04.h │ ├─ myheader03.h │ └─ src │ ├─ includes │ │ └─ myheader02.h │ ├─ main.c │ └─ myheader01.h ***********************************************************/ #include <stdio.h> // 目标文件在同级 // #include "./myheader01.h" #include "myheader01.h" // 目标文件在下一级 // #include "./includes/myheader02.h" #include "includes/myheader02.h" // 目标文件在上一级文件夹 #include "../myheader03.h" // 找到目标文件需要先去到上一级 #include "../inc/myheader04.h" // 目标文件在上上级 #include "../../myheader05.h" // 使用绝对路径 // #include "E:\SH240905\Code\Chapter15\03-myheader.h" #include "E:\vsCode\code\sggtest\chapter\chapter15\03-myheader.h" //写成斜杠/和反斜杠\都可以 int main() { func01(); func02(); func03(); func04(); func05(); func(); return 0; }

条件编译#if

① #if

单向分支

#if 条件 #endif

双向分支

#if 条件 #else #endif

预处理完之后只留下满足条件的那一部分

多向分支

#if 条件 #elif 条件 #elif 条件 #else #endif

② #if defined

判断是否定义了宏名称,定义了才成立

单向分支

#if defined 宏名称 #endif

双向分支

#if defined 宏名称 #else #endif

多向分支

#if defined 宏名称 #elif defined 宏名称 #elif defined 宏名称 #else #endif

③ #ifdef

就是#if define的简写

单向分支

#ifdef 宏名称 #endif

双向分支

#ifdef 宏名称 #else #endif

④ #ifndef

判断是否定义了宏名称,没有定义了才成立(其中的n可以记作not)

单向分支

#ifndef 宏名称 #endif

双向分支

#ifndef 宏名称 #else #endif

条件编译所涉及的预处理器指令

#if #ifdef #ifndef #if defined #else #elif #endif

条件编译应用案例

根据操作系统不同包含不同的头文件

/*********************************************************** * (1)Windows平台下的暂停函数的原型是 void Sleep(DWORD dwMilliseconds),参数的单位是“毫秒”,位于 <windows.h> 头文件。 * (2)Linux平台下暂停函数的原型是 unsigned int sleep (unsigned int seconds),参数的单位是“秒”,位于 <unistd.h> 头文件。 * (3) windows平台通常会预先定义宏常量 _WIN32; linux 平台通常会预先定义宏常量 __linux__ ***********************************************************/ #include <stdio.h> #ifdef _WIN32 #include <windows.h> #define SLEEP(t) Sleep(t*1000) #define PLAYFORM "windows" #elif __linux__ #include <unistd.h> #define SLEEP sleep #define PLAYFORM "Linux" #endif int main(void) { printf("program start .... \n"); //暂停5s SLEEP(5); //打印 printf("Hello,%s\n",PLAYFORM); // 条件分支 ------------------------------------ int num = 600; if (100 > num) { printf("100更大! \n"); } else { printf("100更小! \n"); } // 预处理指令 条件编译----------------------------- #if 100 > num printf("100更大! \n"); #else printf("100更小! \n"); #endif return 0; }

解决头文件重复包含的问题

  • 问题现象:当同一个头文件被多次包含时,会导致函数或变量的重复定义错误
  • 产生原因:预处理指令#include会将头文件内容直接插入源文件,多次包含意味着多次插入相同内容
  • 常见场景:项目结构复杂时,头文件可能被间接包含多次(如A.h包含B.h,同时源文件也直接包含B.h)
  • 保护机制:标准库使用条件编译防止重复包含
  • 工作原理:
    • 第一次包含时_INC_HEADERNAME未定义,执行#define并包含内容
    • 后续包含时因宏已定义,跳过全部内容
  • 命名规则:标准库通常使用__头文件名大写形式__(如_INC_STDIO)

这样子就解决的头文件重复包含的问题了

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/31 5:40:44

U3D|毕设答辩|毕设项目|毕业论文|基于Unity的白族扎染工艺交互设计与开发

文档标题&#xff1a;基于Unity的白族扎染工艺交互设计与开发文档介绍&#xff1a;第1章 绪论1.1 课题研究背景与意义白族扎染是中国云南省大理地区传统的手工印染工艺&#xff0c;已有上千年历史的传承。2006年被列为第一批国家级非物质文化遗产名录。它用针线作笔、布料作纸…

作者头像 李华
网站建设 2026/8/31 5:39:37

英伟达2028预测解读:GPU集群算力规划与TCO管控

英伟达预计 2028 财年销售额达 6730 亿美元&#xff0c;这是近期硬件和 AI 基础设施圈子里被频繁讨论的一条行业预测。很多开发者看到这类数字时&#xff0c;第一反应是股价或市场热度&#xff0c;但作为做技术架构、模型训练平台或数据中心资源规划的人&#xff0c;更应该把它…

作者头像 李华
网站建设 2026/8/31 5:38:59

Linux之socket编程(二)

实现查询字典的功能想要让其完成字典的功能我们要让start使用回调函数主函数server也要有相应的变化最后设计一个字典基本框架loaddict先要把文件加载进来然后我们还需要将//apple: 苹果进行分割,分割符是: 我们先要找到分割符的位置将开头到分隔符--->单词分隔符到最后---&…

作者头像 李华
网站建设 2026/8/31 5:38:57

安全厂商运维实战:从终端安全、国产系统到Kubernetes容器化

做奇安信运维工程师这几年&#xff0c;总有朋友问我&#xff0c;在安全公司做运维是不是特别刺激&#xff0c;天天跟攻击者打交道。说实话&#xff0c;大部分时间我面对的不是网络攻击&#xff0c;而是告警、工单、终端agent、国产操作系统适配&#xff0c;还有一堆“拆不掉”的…

作者头像 李华
网站建设 2026/8/31 5:38:07

LeetCode高效刷题指南:掌握核心算法模式与系统化训练方法

你有没有过这样的经历&#xff1a;打开 LeetCode&#xff0c;面对上千道题目&#xff0c;从哪开始刷&#xff1f;刷到什么程度才算够&#xff1f;刷完一遍&#xff0c;过两周再看到同类题&#xff0c;思路又卡壳了&#xff1f;这几乎是每个技术人准备面试、提升算法能力时都会遇…

作者头像 李华