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

C语言初学者笔记【预处理】

文章目录

  • 前言
  • 一、预定义符号
  • 二、#define定义常量
  • 三、#define定义宏
  • 四、带有副作用的宏参数
  • 五、宏替换规则
  • 六、宏 vs 函数
  • 七、#和##运算符
    • 1. #运算符(字符串化)
    • 2.##运算符(记号粘合)
  • 八、命名约定
  • 九、#undef
  • 十、命令行定义
  • 十一、条件编译
    • 1.基本形式:
    • 2.多分支:
    • 3.判断是否定义:
  • 十二、头文件包含
    • 1. 包含方式
    • 2. 防止重复包含
    • 3. 其他预处理指令
  • 总结


前言

我们用C语言直接写出来的代码是不能被计算机进行识别的,这其中需要进行一系列过程使源码转换成计算机所能识别的二进制语言,这一系列过程就叫做翻译。源码翻译过程主要有四步:

预处理:头文件展开,去注释,宏替换,条件编译等

编译:将C语言翻译成汇编语言

汇编:将汇编语言转化为可重定向目标文件(可被链接,已经是二进制,但不是可执行文件)

链接:自身程序+库文件进行关联,形成可执行程序

本篇文章我们就来谈谈预处理这个环节,并了解如何定义宏,如何进行条件编译。

一、预定义符号

C语言提供了一些内置的预定义符号,可用于获取编译环境信息:

· FILE:当前源文件名
· LINE:当前行号
· DATE:编译日期
· TIME:编译时间
· STDC:如果编译器遵循ANSI C,值为1,否则未定义

二、#define定义常量

语法:#define name stuff

示例:

#define MAX 1000
#define reg register
#define do_forever for(;;)
#define CASE break;case

注意:不要在#define结尾加分号,否则可能导致语法错误。

三、#define定义宏

语法:#define name(parameter-list) stuff

示例:

#define SQUARE(x) x*x        // 有问题:SQUARE(a+1) → a+1*a+1
#define SQUARE(x) (x)*(x)    // 正确:加上括号

重要提示:宏定义中的每个参数和整个表达式都应该用括号括起来,避免运算符优先级问题。

四、带有副作用的宏参数

副作用:表达式求值时产生的永久性效果(如x++)

示例:

#define MAX(a, b) ((a) > (b) ? (a) : (b))
x = 5; y = 8;
z = MAX(x++, y++); // 展开后:((x++) > (y++) ? (x++) : (y++))
// 结果:x=6, y=10, z=9

五、宏替换规则

  1. 调用宏时,先检查参数是否包含#define定义的符号,如有则先替换
  2. 替换文本插入到程序中原位置
  3. 再次扫描结果,查看是否包含其他#define定义的符号

注意:

· 宏不能递归
· 字符串常量中的内容不会被搜索替换

六、宏 vs 函数

属性 宏 函数
代码长度 每次使用都插入代码,增加程序长度 代码只出现一次
执行速度 更快(无调用开销) 有调用返回开销
操作符优先级 可能产生问题,需加括号 参数求值结果明确
副作用参数 参数可能被多次计算 参数只求值一次
参数类型 类型无关 类型相关
调试 不方便 可逐语句调试
递归 不能 可以

宏的优势:可处理类型参数

#define MALLOC(num, type) (type*)malloc(num * sizeof(type))
MALLOC(10, int); // 展开为:(int*)malloc(10 * sizeof(int))

七、#和##运算符

1. #运算符(字符串化)

将宏参数转换为字符串字面量

#define PRINT(n) printf("the value of "#n" is %d", n)
PRINT(a); // 输出:the value of a is [a的值]

2.##运算符(记号粘合)

将两边的符号合并为一个标识符

#define GENERIC_MAX(type) \
type type##_max(type x, type y) { return x > y ? x : y; }GENERIC_MAX(float) // 生成函数:float float_max(float x, float y)

八、命名约定

· 宏名:全部大写(如#define MAX_SIZE 100)
· 函数名:不要全部大写

九、#undef

用于移除已定义的宏:

#undef NAME

十、命令行定义

许多编译器允许在命令行中定义符号,用于根据不同需求编译不同版本的程序。

十一、条件编译

根据条件决定是否编译某段代码:

1.基本形式:

#if 常量表达式//...
#endif

2.多分支:

#if 常量表达式//...
#elif 常量表达式//...
#else//...
#endif

3.判断是否定义:

#ifdef SYMBOL    // 或 #if defined(SYMBOL)//...
#endif#ifndef SYMBOL   // 或 #if !defined(SYMBOL)//...
#endif

十二、头文件包含

1. 包含方式

· 本地文件:#include “filename.h”(先当前目录,后系统目录)
· 库文件:#include <filename.h>(直接系统目录)

2. 防止重复包含

方法1(传统方式):

#ifndef __HEADER_H__
#define __HEADER_H__
// 头文件内容
#endif

方法2(编译器扩展):

#pragma once
// 头文件内容

3. 其他预处理指令

· #error:生成编译错误信息
· #pragma:编译器特定指令
· #line:改变当前行号和文件名
· #pragma pack():在结构体部分介绍(用于内存对齐)

总结

预处理是C语言编译过程中的重要阶段,合理使用预处理指令可以:

  1. 提高代码可读性和可维护性
  2. 实现条件编译和跨平台支持
  3. 提高代码复用性
  4. 优化程序性能(通过宏替代小函数)

但需要注意宏可能带来的副作用和优先级问题,建议:

· 宏参数和整个表达式都用括号括起来
· 避免使用带有副作用的参数
· 遵循命名约定区分宏和函数



文章转载自:

http://HAqRDg7A.xgLgm.cn
http://V1UV7h3N.xgLgm.cn
http://5Iq7o7LM.xgLgm.cn
http://ftwgj2yt.xgLgm.cn
http://PznKNUEC.xgLgm.cn
http://wnQRlpD1.xgLgm.cn
http://QNJBhgAn.xgLgm.cn
http://1pr9ZKqL.xgLgm.cn
http://L8LFDcDN.xgLgm.cn
http://3uCxS1a0.xgLgm.cn
http://tDrS8Kar.xgLgm.cn
http://rkh7BVf9.xgLgm.cn
http://NThahlHj.xgLgm.cn
http://S7Ajmy7Y.xgLgm.cn
http://QzYvxrPG.xgLgm.cn
http://Fs9wIAmZ.xgLgm.cn
http://4AWuKqAk.xgLgm.cn
http://S4U5JKmQ.xgLgm.cn
http://DqPkeoOY.xgLgm.cn
http://VMgXX8Zt.xgLgm.cn
http://GVk1iNLn.xgLgm.cn
http://J0Sm4bXE.xgLgm.cn
http://myHiX8AH.xgLgm.cn
http://uUBaDJWo.xgLgm.cn
http://x1IRQy0K.xgLgm.cn
http://0MowCqSd.xgLgm.cn
http://VHo3fXt5.xgLgm.cn
http://N8LyIuiH.xgLgm.cn
http://EWqlRZGd.xgLgm.cn
http://gjnZvRFk.xgLgm.cn
http://www.dtcms.com/a/380611.html

相关文章:

  • android中ViewModel 和 onSaveInstanceState 的最佳使用方法
  • 达梦:将sql通过shell脚本的方式放在后台执行
  • 进阶向:从零开始理解Python音频处理系统
  • Centos7安装nginx
  • 数字图像处理-巴特沃斯高通滤波、低通滤波
  • Knockout数据绑定语法的入门教程
  • Serdes专题(1)Serdes综述
  • 2025年机器人项目管理推荐:三款工具破解机械设计到量产交付的协同难题
  • 后端post请求返回页面,在另一个项目中请求过来会出现的问题
  • 前端菜单权限方案
  • 【运维】-- 前端会话回放与产品分析平台之 openreplay
  • 前后端开发Mock作用说明,mock.ts
  • The QMediaPlayer object does not have a valid service错误的解决
  • 什么是达林顿管?
  • 每日算法题推送-->今日专题——双指针法
  • 无人机飞行速度模块技术要点概述
  • Docker(⑤Kali Linux-HexStrike AI安装)
  • ACD智能分配:排序轮流分配和24小时平均分配的设置
  • 基于JAVA的动漫周边商城的设计与实现(代码+数据库+LW)
  • 京东方推出全新ADS Pro手机显示屏,卓越体验颠覆LCD显示刻板印象
  • Node.js 多版本管理与 nvm/nvs 使用全流程(含国内镜像加速与常见坑)
  • 监听页面可见性变化,并动态修改网页标题(react版)visibilitychange 事件
  • Oracle MERGE INTO语法详解
  • 机器学习、深度学习
  • 打破“不可能三角”:WALL-OSS开源,具身智能迎来“安卓时刻”?
  • OpenCV的特征检测
  • 基于CNN/CRNN的汉字手写体识别:从图像到文字的智能解码
  • 非标自动化工厂如何10个三维设计共用一台云主机
  • Jupyter Notebook操作指南(1)
  • 远程连接Mac操作ClaudeCode一直提示登录Invalid API key · Please run /login