首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >[c语言日寄]预处理命令详解

[c语言日寄]预处理命令详解

作者头像
siy2333
发布于 2025-04-06 13:16:02
发布于 2025-04-06 13:16:02
9800
代码可运行
举报
文章被收录于专栏:来自csdn的博客来自csdn的博客
运行总次数:0
代码可运行

前言

在C语言的开发过程中,预处理命令是一个不可或缺的部分。预处理命令在编译过程的早期阶段发挥作用,它们帮助我们实现代码的模块化、条件编译、宏定义等功能,从而提高代码的可读性、可维护性和灵活性。今天,我们就来深入探讨C语言中的预处理命令,从基础知识到实际应用,帮助你更好地理解和使用它们。


知识点分析

一、程序的翻译环境和执行环境

在ANSI C的任何一种实现中,存在两种不同的环境:翻译环境和执行环境。

  1. 翻译环境
    • 源代码被转换为可执行的机器指令。
    • 包括编译环境和链接环境。
    • 在将.c文件转换为.exe时依赖的环境。
  2. 执行环境
    • 实际执行代码的环境。

二、详解编译与链接

翻译流程
  1. 编译
    • 预编译
      • 文本操作:
        1. #include 头文件的包含。
        2. #define 定义符号的替换和删除。
        3. 注释的删除。
    • 编译
      • 把C语言代码翻译为汇编代码。
      • 包含的操作:
        1. 语法分析。
        2. 词法分析。
        3. 语义分析
        4. 符号汇总:记录全局变量、函数名等。
    • 汇编
      • 将汇编代码转换为二进制指令。
      • 形成符号表:全局变量名、函数名、对应的地址形成符号表,但不包含局部变量。
  2. 链接
    • 作用
      • 合并段表。
      • 符号表的合并和重定位。
      • 通过符号表中符号和地址的对应关系进行处理。
  3. extern
    • 声明外部符号。
  4. 运行环境
    • 程序的运行过程:
      1. 将程序载入内存(一般由操作系统完成)。
      2. 程序的执行开始,接着调用main函数。
      3. 程序开始执行代码:
        • 使用运行时堆栈存储局部变量和返回地址。
        • 使用静态内存存储全局变量,这些变量在程序的整个执行过程中一直保留其值。
      4. 程序终止(正常终止和意外终止)。

三、预处理详解

预定义符号

  • 两个连续下划线__

#define

定义标识符

  • 注意:
    • 一般不添加分号。
    • 可以多行写。

定义宏

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define MAX(a, b) ((a) > (b) ? (a) : (b))

注意:

  • 参数列表的左括号必须与名称相邻。
  • 宏是替换,要注意计算优先级,最好带括号。

#define的替换规则

  • 先检查宏里的参数,先替换参数。
  • 替换文本到程序中。
  • 再对结果文件进行扫描。

注意事项

  • 宏里面不能递归。
  • 宏里面可以出现其他#define定义的符号。
  • 字符串常量里的内容不会被搜索,因此不会被替换。

###

基于#的字符串替换宏

原理:

  • 连续的两个字符串会被相连,然后看成一个字符串。
  • 对于一个宏,在被引用的参数前添加#,代表这个参数以字符串的形式替换。

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define STRINGIFY(x) #x

基于##的字符串拼接宏

##的作用:把两边的符号合成一个符号。

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define PASTE(x, y) x ## y

带有副作用的宏参数

宏参数在宏定义中出现超过一次时,如果参数带有副作用,那么在使用宏的时候会出现危险,导致不可预测的结果。

原理:

  • a++++a会对a产生影响。
  • 宏是替换,函数是传参。

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define SQUARE(x) ((x) * (x))
int a = 5;
int result = SQUARE(a++); // 危险:a++被替换两次

函数和宏的对比

  • 宏的优势
    • 执行宏比函数消耗的时间更小。
      • 函数消耗的时间:
        1. 函数调用。
        2. 函数运算的执行。
        3. 函数返回。
    • 宏是替换,宏不检查类型。
    • 宏的参数可以传递类型。
  • 宏的缺点
    • 由于是替换,如果多次使用很长的宏,会导致代码长度大幅变长。
    • 宏无法调试。
      • 调试是在可以执行程序阶段调试的,而宏是在预处理阶段完成替换的。
      • 你看到的代码是宏语句,但实际上已经被替换为宏对应的指令,看到和实际上执行的代码不一致。
    • 宏与类型无关,不够严谨。
    • 宏容易带来运算优先级的问题,容易出错。
      • 解决方案:多带括号,不要吝啬括号。
  • 具体对比
    • 宏适合用于小型运算,如MAX(a, b)
    • 函数适合用于复杂的逻辑处理。

命名约定

  • 宏名全部大写。
  • 函数名不要全部大写。

#undef

  • 移除宏定义。
  • 如果现存的名称需要被重新定义,那么首先需要移除旧的定义。

命令行定义

  • 在许多C的编译器中,允许在命令行中定义符号,用于启动编译功能。
  • 应用:
    • 同一个源文件程序编译出不同版本时。

条件编译

条件编译指令

如果常量表达式为真,中间参与编译,否则,中间不参与编译。

多分支的条件编译。

判断是否被定义。

放在一起的两个等价:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#ifdef SYMBOL
#ifndef SYMBOL

嵌套指令。

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#ifdef DEBUG
printf("Debug mode\n");
#endif

文件包含

  • 文件被包含的方式
    • 本地文件包含
      • 查找策略:
        • 先在源文件所在目录下查找,如果没找到,编译器就在标准位置查找头文件。
        • 两个位置都找不到就报错。
    • 库函数包含
      • 直接去标准位置查找。
  • 风险
    • 重复包含。 在大型工程中常见的错误。
    • 解决方案:
      1. 使用条件编译。
      2. 使用#pragma once,在头文件开头。

其他预处理指令

#line:修改当前文件名和行号。

#error:生成编译错误。

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#error "This is an error message"

注意事项

  1. 宏的使用
    • 宏是替换,不是函数调用,因此要注意运算优先级。
    • 宏的参数可能会被多次替换,导致副作用。
    • 宏的定义和使用要谨慎,避免引入错误。
  2. 条件编译
    • 条件编译指令的使用要清晰,避免嵌套过深。
    • 使用条件编译时,要确保代码的可读性。
  3. 文件包含
    • 避免重复包含头文件,使用#pragma once或条件编译。
    • 包含的头文件路径要正确,避免找不到文件的错误。
  4. 命令行定义
    • 命令行定义的符号要明确,避免冲突。
    • 使用命令行定义时,要确保编译器支持。
  5. 调试
    • 宏无法调试,因此在调试阶段,可以将宏替换为函数。
    • 使用调试工具时,要注意宏的替换结果。

拓展应用

1. 宏的高级应用

调试宏

定义一个调试宏,用于在调试模式下打印变量的值。

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#ifdef DEBUG
#define DEBUG_PRINT(x) printf("Debug: " #x " = %d\n", x)
#else
#define DEBUG_PRINT(x)
#endif

条件编译的高级应用

使用条件编译来实现不同平台的代码。

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#ifdef _WIN32
// Windows-specific code
#elif defined(__linux__)
// Linux-specific code
#else
// Other platforms
#endif

2. 文件包含的高级应用

模块化开发

将不同的功能模块分别放在不同的头文件中,通过条件编译来选择性地包含。

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#ifdef MODULE_A
#include "module_a.h"
#endif

#ifdef MODULE_B
#include "module_b.h"
#endif

避免重复包含

使用#pragma once或条件编译来避免头文件的重复包含。

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// header.h
#pragma once
#ifndef HEADER_H
#define HEADER_H
// Header content
#endif

3. 预处理指令的高级应用

生成编译错误

使用#error指令来生成编译错误,提示用户某些条件未满足。

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#if defined(_WIN32) && !defined(_DEBUG)
#error "Debug mode must be enabled on Windows"
#endif

修改文件名和行号

使用#line指令来修改当前文件名和行号,用于调试或日志记录。

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#line 100 "custom_file.c"

总结

预处理命令是C语言中非常重要的部分,它们在编译过程的早期阶段发挥作用,帮助我们实现代码的模块化、条件编译、宏定义等功能。通过合理使用预处理命令,可以提高代码的可读性、可维护性和灵活性。

关注窝,每三天至少更新一篇优质c语言详解~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-04-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
server 2016 安装WDS服务并下发操作系统
WDS的全称为Windows部署服务,主要应用于大中型网络中的计算机操作系统的批量化部署。通过使用WDS可以管理系统映像和无人参与安装脚本,并提供人工参与安装和无人参与安装的方式,大大提升我们安装操作系统的速度。 首先,部署WDS需要满足一下条件: 1、最好使用专业级服务器作为分发端,如果使用普通计算机,可能会导致分发过程不稳定,降低整体分发效率。客户机需要网卡支持PXE功能(PXE功能一般的计算机都支持),服务器和客户机必须在同一局域网。如果部署超过百台的计算机时,优先使用千兆网络。 2、DHCP:用于向需要安装操作系统的客户机分配ip地址。(部署DHCP服务在之前的文章有提到,需要的话可以参考一下) 3、AD服务:活动目录服务,主要用于用户身份的验证(WDS服务有两种部署方式,“与AD集成模式”和“独立模式”。如果使用独立模式,则不需要安装AD服务) 4、服务器必须是有管理员权限,并准备好想要安装的系统镜像,(最好是纯净版的,在网上随便下载的镜像,都经过别人的篡改。可能无法部署该服务) 好,具备以上要求,我们就开始部署该服务。 首先我们先打开服务器管理器
小手冰凉
2019/09/10
1.6K0
server 2016 安装WDS服务并下发操作系统
Win系统安装部署流程与工具
描述:Windows Deployment Service即windows部署服务,我们可以通过windows server自带的部署服务通过网络将操作系统部署到每台计算机上,并且可以通过WDS来管理多版本映像以及无人参与安装脚本,和网刻非常相似不过这个更简单操作更方便
全栈工程师修炼指南
2020/10/26
2.9K0
Win系统安装部署流程与工具
Windows server——部署DHCP服务(2)
本章将会讲解如何配置DHCP服务,安装DHCP服务,配置DHCP客户端,维护DHCP,监视DHCP
网络豆
2023/10/15
4.1K0
Windows server——部署DHCP服务(2)
Windows Server 2016搭建DNS服务
今天跟大家简单介绍一下如何在Windows Server 2016 上搭建DNS(域名解析)服务。 DNS服务器要为客户端提供域名解析服务,必须要具备以下条件: 1.有固定的IP地址; 2.安装并启动DNS服务; 3.有区域文件,配置转发器或配置根提示。 满足条件后,下面开始安装DNS服务 1.首先确认本机的IP地址
星哥玩云
2022/07/28
6.1K0
Windows Server 2016搭建DNS服务
WIndows 2016 部署WDS
WDS 是 Windows 部署服务的缩写,它是一种用于部署 Windows 操作系统的服务。WDS 可以通过网络将 Windows 安装到新计算机上,而无需管理员访问每台计算机。
神秘泣男子
2024/06/03
2220
WIndows 2016 部署WDS
Windows server——部署DNS服务(2)
本章将会讲解Windows server 配置DNS服务。前期回顾:Windows server——部署DNS服务
网络豆
2023/10/15
1.7K0
Windows server——部署DNS服务(2)
windows网络批量安装WDS+win10自动应答文件
在上篇文章中,有同学评论想要“网络安装微软原盘镜像”的教程,恰好我也会,所以本次教程为windows部署服务+自动应答,内容以实用为主,不涉及细节讲解,因windows的复杂性,详细内容建议直接到微软文档库查看。
王忘杰
2022/09/21
2.3K0
windows网络批量安装WDS+win10自动应答文件
自动化批量安装Windows11——Windows Servcer 2022 MDT
Windows Server 2022和Windows 11都发布一段时间了,使用的客户也渐渐多了起来,今天来讲解一下,如何利用Windows Server 2022来批量化地自动部署Windows 11。
IT狂人日志
2022/05/18
3.9K0
自动化批量安装Windows11——Windows Servcer 2022 MDT
PXE及PXE启动
PXE(Pre-boot Execution Environment)是由Intel设计的协议,它可以使计算机通过网络启动。协议分为client和server两端,PXE client在网卡的ROM中,当计算机引导时,BIOS把PXE client调入内存执行,并显示出命令菜单,经用户选择后,PXE client将放置在远端的操作系统通过网络下载到本地运行。 PXE协议的成功运行需要解决以下两个问题: 既然是通过网络传输,那么计算机在启动时,它的IP地址由谁来配置; 通过什么协议下载Linux内核和根文件系统 对于第一个问题,可以通过DHCP Server解决,由DHCP server来给PXE client分配一个IP地址,DHCP Server是用来给DHCP Client动态分配IP地址的协议,不过由于这里是给PXE Client分配IP地址,所以在配置DHCP Server时,需要增加相应的PXE特有配置。 至于第二个问题,在PXE client所在的ROM中,已经存在了TFTP Client。PXE Client使用TFTP Client,通过TFTP协议到TFTP Server上下载所需的文件。 这样,PXE协议运行的条件就具备了,下面我们就来看看PXE协议的工作过程。 工作过程 在上图中,PXE client是需要安装Linux的计算机,TFTP Server和DHCP Server运行在另外一台Linux Server上。Bootstrap文件、配置文件、Linux内核以及Linux根文件系统都放置在Linux Server上TFTP服务器的根目录下。 PXE client在工作过程中,需要三个二进制文件:bootstrap、Linux 内核和Linux根文件系统。Bootstrap文件是可执行程序,它向用户提供简单的控制界面,并根据用户的选择,下载合适的Linux内核以及Linux根文件系统。 步骤 有了前面的背景知识,接下来就可以正式操作了,下面按照顺序给出了操作步骤: 配置DHCP Server 选用ISC dhcp-3.0,DHCP Server的配置文件是/etc/dhcpd.conf,配置文件的内容如下:
全栈程序员站长
2022/07/02
4K0
Windows server环境下混合云部署DFS文件服务
很多传统企业在设计身份验证和文件服务时,都会使用微软的活动目录和windows server自带的文件共享来实现以上的需求, 公有云的出现给这个场景在灾备和异地访问提供了一个全新的方案场景。
用户2225761
2018/11/23
3.9K0
Windows server环境下混合云部署DFS文件服务
COBBLER批量安装Windows系统
需求:采用Cobbler批量分发安装Windows10(官方原版未封装过)并默认进入审核模式;
全栈工程师修炼指南
2020/10/26
5K1
COBBLER批量安装Windows系统
Windows server——部署DNS服务(3)
本章将会继续讲解《Windows server——部署DNS服务》前期回顾Windows server——部署DNS服务,Windows server——部署DNS服务(2)
网络豆
2023/10/15
1.2K0
Windows server——部署DNS服务(3)
Active Directory与域服务,介绍,安装
Active Directory是一种由微软开发的网络服务,用于管理用户、计算机和其他网络资源,是企业网络的核心目录服务。它提供了一种集中管理和控制企业网络资源的方法,包括用户、计算机、应用程序、安全策略等。通过Active Directory,管理员可以轻松地集中管理和控制网络上的所有资源,确保网络的高可用性、安全性和一致性。
网络豆
2023/10/17
1.5K0
Active Directory与域服务,介绍,安装
扔掉U盘,网启PE,桌面维护的摸鱼利器
注意,本文对读者有一定选择性,如果你并非是企业桌面维护,则本文可以略过,请点击作者头像查看其他精彩文章~
王忘杰
2022/09/22
1.2K0
扔掉U盘,网启PE,桌面维护的摸鱼利器
Windows网络服务与配置管理之活动目录学习
活动目录英文全称为“Active Directory”,简称为AD。在windows系统组成的网络中,有服务器、客户机、用户账户、打印机、各种文件,这些资源都在各台计算机上,没有使用活动目录之前需要在各台计算机上单独管理这些资源。使用活动目录可以集中管理windows网络各类资源,"活动目录"就像一个数据库,存储着windows网络中的所有资源。
黑白天安全
2020/06/23
4.2K0
Windows网络服务与配置管理之活动目录学习
GNS3环境搭建与测验实战图文详解
GNS3是一款具有图形化界面可以运行在多平台(包括Windows, Linux, and MacOS等)的网络虚拟软件。Cisco网络设备管理员或是想要通过CCNA,CCNP,CCIE等Cisco认证考试的相关人士可以通过它来完成相关的实验模拟操作。同时它也可以用于虚拟体验Cisco网际操作系统IOS或者是检验将要在真实的路由器上部署实施的相关配置。
星哥玩云
2022/07/28
3K0
GNS3环境搭建与测验实战图文详解
VMware ESXI 5.0群集+ISCSI存储
运行VMware Workstatioin的机器必须拥有64比特的CPU,而CPU也必须支持VT-X技术
月缺
2020/01/15
1.4K0
VMware ESXI 5.0群集+ISCSI存储
PXE远程网络装机服务及CentOS 7无人值守安装
在大规模的Linux应用环境中,比如Web群集、分布式计算等,服务器往往并不配备光驱设备,在这种情况下,如何给成百上千台服务器裸机快速安装系统呢?传统的USB光驱、移动硬盘等安装方式很显然已经力不从心了,那么就需要——PXE网络装机及无人值守安装。
用户8705050
2021/06/08
2.1K0
域渗透基础之环境搭建(单域到组件域林)
转发:https://www.e-learn.cn/content/qita/2484245
墨文
2020/02/28
1.8K0
域渗透基础之环境搭建(单域到组件域林)
PXE网络
PXE 严格来说并不是一种安装方式,而是一种引导的方式。进行 PXE 安装的必要条件是要安装的计算机中包含一个 PXE 支持的网卡(NIC),即网卡中必须要有 PXE Client。PXE (Pre-boot Execution Environment)协议使计算机可以通过网络启动。 协议分为 client 和 server 端,PXE client 在网卡的 ROM 中,当计算机引导时,BIOS 把 PXE client 调入内存执行,由 PXE client 将放置在远端的文件通过网络下载到本地运行。运行 PXE 协议需要设置 DHCP 服务器 和 TFTP 服务器。DHCP 服务器用来给 PXE client(将要安装系统的主机)分配一个 IP 地址,由于是给 PXE client 分配 IP 地址,所以在配置 DHCP 服务器时需要增加相应的 PXE 设置。 此外,在 PXE client 的 ROM 中,已经存在了 TFTP Client。PXE Client 通过 TFTP 协议到 TFTP Server 上下载所需的文件。
全栈程序员站长
2022/07/04
1.5K0
PXE网络
相关推荐
server 2016 安装WDS服务并下发操作系统
更多 >
LV.5
这个人很懒,什么都没有留下~
目录
  • 前言
  • 知识点分析
    • 一、程序的翻译环境和执行环境
    • 二、详解编译与链接
      • 翻译流程
    • 三、预处理详解
  • 注意事项
  • 拓展应用
    • 1. 宏的高级应用
    • 2. 文件包含的高级应用
    • 3. 预处理指令的高级应用
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档