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

C/C++:预处理(下)

目录

一.回顾程序的编译链接过程

二. 预处理之预定义#define

1.#define定义的标识符

2.#define定义的宏

3.带副作用的表达式作为宏实参 

4.两个经典的宏

5.#define使用的一些注意事项小结

6.宏与函数的比较

7.#undef

附:关于#define的三个冷知识

三. 条件编译

四.预处理之#include

1.#include<> 与  #include" "

2.头文件的重复包含问题


一.回顾程序的编译链接过程

二. 预处理之预定义#define

1.#define定义的标识符

#define定义的标识符源码文件预处理阶段会以文本替换的方式被替换为定义的内容

比如:

#define MAX 1000
//MAX 是被宏定义的标识符
//MAX空格后的所有内容是其定义的内容           

注意:

  • 标识符是以空格为结尾的(也就是说#define定义的标识符中不含有空格)
  • #define语句最后不要加上; 不然分号也会被替换到代码段中造成一些bug

2.#define定义的宏

#define定义的标识符可以带参数(和函数有点类似),#define定义的带参标识符称为

比如:

#define N 4
#define Y(n) ((N+2)*n)z = 2 * (N + Y(5+1));  //z最后的结果是多少?
  • Y(n)就是一个宏,Y是宏名,n是宏的参数
  • z的结果分析:

因此z最后算出来的结果为70,可见该式子中5+1并没有优先被计算,所以用于对数值表达式进行求值的宏定义一定要注意将宏参数和宏体都用括号括起来避免因为运算符优先级问题导致运算结果不符合预期,因此宏Y(n)更严谨的写法应该是:

#define Y(n) (((N)+2)*(n))

3.带副作用的表达式作为宏实参 

#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
...
x = 5;
y = 8;
z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);//输出的结果是什么?

因此最终:z=9,x=6,y=10.(x自增了两次,y自增了一次)

  • 可见带副作用的表达式(副作用就是表达式求值的时候相关变量的值被改变)作为宏实参是十分危险的

4.两个经典的宏

  • 百度工程师笔试题:写一个宏,计算结构体中某变量相对于首地址的偏移(offsetof宏的实现)(宏的实参可以是变量类型名)
#define OFFSETOF(structname,numbername) (size_t)&(((structname *)0)->numbername)

#define OFFSETOF(structname,numbername) (size_t)&(((structname *)0)->numbername)//宏的测试
typedef struct Node
{int i;char c;short d;
}Node;
int main ()
{printf("%u\n",OFFSETOF(Node,i));printf("%u\n",OFFSETOF(Node,c));printf("%u\n",OFFSETOF(Node,d));return 0;
}

这是类型名作为宏参数的一个典型应用。

关于结构体成员偏移量参见结构体内存对齐: http://t.csdn.cn/Vd6ix

  • 一个经典算法宏:写一个宏,可以将一个正整数(32比特位)的二进制补码的奇数位和偶数位交换 

比如:

#define EXCHANGEBIN(NUM) (((NUM)&(0x55555555))<<1)|(((NUM)&(0xaaaaaaaa))>>1)

算法解析:

#include <stdio.h>#define EXCHANGEBIN(NUM) (((NUM)&(0x55555555))<<1)|(((NUM)&(0xaaaaaaaa))>>1)//宏测试int main()
{int num = 426;                      //二进制补码为:0000 0000 0000 0000 0000 0001 1010 1010printf("%u\n",EXCHANGEBIN(num));    //转换后补码为:0000 0000 0000 0000 0000 0010 0101 0101return 0;
}

 

 

5.#define使用的一些注意事项小结

  • 带有副作用的表达式(会改变变量的值)作为宏的实参时要注意其在宏体中出现的次数带来的影响
  • 宏体的定义中要多使用括号来清楚地表示运算的结合性
  • 宏的参数可以是任意类型的变量甚至可以是类型名,使用时要注意合理的类型匹配
  • 宏的文本替换机制会使代码的可维护性降低(被替换到源码文本中的宏体会与源码的上下文环境产生难以预料的相互作用),比较复杂的过程避免使用宏来封装
  • 宏体在预编译阶段被替换到源码文本中,代码执行调试时,用户看到的源码段和实际被调试的代码段有所差异
  • 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索

    比如:

    #define N 4
    char arr[]="N";
    //arr中的N不会被替换

6.宏与函数的比较


#define EXCHANGEBIN(NUM) (((NUM)&(0x55555555))<<1)|(((NUM)&(0xaaaaaaaa))>>1)int ExchangeBin(int num)
{return (((num)&(0x55555555))<<1)|(((num)&(0xaaaaaaaa))>>1);
}int main()
{int num = 426;                  EXCHANGEBIN(num);  ExchangeBin(num);return 0;
}

代码段中的宏和函数实现了相同的功能,但是实际上执行宏和执行函数的汇编指令会有比较大的差异。

执行EXCHANGEBIN(num)宏的汇编指令:

执行ExchangeBin(num)函数对的汇编指令:

  • 可见实现相同的功能,执行宏的指令段比执行函数的指令段要简洁很多,因此对于一些简单且在程序中被频繁使用的表达式而言,使用宏来对其进行封装会让程序运行效率更高
  • 由于宏是文本替换,所以经过预处理后,宏可能会使源码的文本长度大幅增加,使程序运行时占用的内存更大,而函数不会有这样的问题,因为一个函数的函数体内存的只读常量区只会存储一份。
  • 宏的参数没有类型限制(甚至可以是类型名),而函数的参数会有严格的类型检查,在这一点上函数更加安全
  • 宏体难以进行逐语句调试(源文件的宏调用语句中无法看到展开的宏体),而函数可以进行逐语句调试
  • 宏不能递归,函数可以递归

7.#undef

#undef指令用于移除一个宏定义

#undef NAME

附:关于#define的三个冷知识

  1. 使用 # ,可以把一个宏参数变成对应的字符串
    int i = 10;
    #define PRINT(FORMAT, VALUE)\            //宏体可以分行定义(用反斜杠加回车将宏体内容换行)
    printf("the value of " #VALUE "is "FORMAT "\n", VALUE);int main()
    {PRINT("%d", i+3);  // #VALUE会使参数 i+3 变为对应的字符串"i+3"
    }
    
  2. ##可以把位于它两边的符号合成一个符号。
    它允许宏定义从分离的文本片段创建标识符(标识符必须预先定义好)。
    #define ADD_TO_SUM(num, value) \
    sum##num += value;int main ()
    {int sum5 =10;ADD_TO_SUM(5, 10); //预处理将该语句替换为 sum5 += 10
    }
    
  3. gcc的命令行定义:
    #include <stdio.h>
    int main()
    {int array [ARRAY_SIZE];return 0;
    }

    我们可以在gcc的编译命令行中指定常量ARRAY_SIZE的值:

    gcc -D ARRAY_SIZE=10 -E ./testproject/test.c -o test.i

三. 条件编译

  1. 常量表达式条件编译:

    //单分支条件编译指令
    #if 常量表达式//代码段#endif
    
    //多分支条件编译指令
    #if   常量表达式//代码段#elif 常量表达式//代码段#else//代码段#endif//#elif 可以类比 else if 来理解

    当常量表达式为真则对#if和#endif(或#elif,#else)之间的代码段执行编译,为假则编译器会自动屏蔽掉#if和#endif之间的代码段(或#elif,#else)(常量表达式由预处理器求值)

  2. #define的条件编译

    ​#ifdef symbol//代码段     #enif如果symbol被#define定义了,则编译器会编译代码段,如果没有symbol没有被#define定义,则编译器不会编译代码段​
    #ifndef symbol//代码段    #endif如果symbol没有被#define定义则编译器会编译代码段,如果symbol被#define定义了则编译器不会编译代码段
条件编译的嵌套
#ifdef OS_Unix#ifdef OPTION1unix_version_option1();#endif#ifdef OPTION2unix_version_option2();#endif#elifdef OS_MSDOS#ifdef OPTION2msdos_version_option2();#endif#endif

 条件编译在一些项目中以及语言标准库源码中很常见。

四.预处理之#include

#include的作用是将指定头文件中的内容"复制粘贴"到当前源文件

1.#include<> 与  #include" "

  • 本地文件包含指令
    #include "filename"

    编译器查找方式:编译器会先在当前源文件所在路径下查找filename文件,如果该头文件未找到,编译器就会到标准库路径下查找filename文件。如果找不到就提示编译错误

  • 库文件包含指令
     

    #include <filename>

    编译器会直接去标准库路径下去查找filename文件,如果找不到就提示编译错误

根据具体情况选择对应的文件包含指令可以提高编译器的编译效率,并且可以在源码层面上令人更容易区分库文件和本地文件

2.头文件的重复包含问题

头文件在同一个源文件中的重复包含问题

一个复杂的项目工程中很容易出现这样的场景:

上面的场景中comm.h在test.c中重复被包含了两次,意味着comm.h中相同的内容会两次被"复制粘贴"到test.c中,这可能会导致程序链接错误(这中链接错误往往会折腾人半天)

  • 在头文件中加上#pragma once指令可以避免该头文件被重复包含到同一个源文件
    #pragma once

    每个头文件中都加上#pragma once指令是良好的编程习惯

同一个头文件多个源文件中的被包含问题

  • 一个头文件往往会被多个源文件同时包含,因此头文件中要避免出现全局变量的定义和函数体的定义,不然相同的变量(或函数)的定义会在全局域中出现多次而导致链接错误。
  • 全局标识名声明和定义分离,声明统一放在头文件中,定义统一放在源文件中,这是必备的编程素养

 

 

 

 

 

相关文章:

C/C++:预处理(下)

目录 一.回顾程序的编译链接过程 二. 预处理之预定义#define 1.#define定义的标识符 2.#define定义的宏 3.带副作用的表达式作为宏实参 4.两个经典的宏 5.#define使用的一些注意事项小结 6.宏与函数的比较 7.#undef 附&#xff1a;关于#define的三个冷知识 三. 条件…...

2023互联网相关岗位转行与就业选择的简单分析

文章目录1、城市2、岗位1、城市 能找得到工作的城市&#xff0c;可能主要也就这些base了 2、岗位 主要技术岗位 Python 侧重人工智能&#xff0c;人工智能门槛高大家心知肚明。如果学python 不走人工智能&#xff0c;只走单纯的后端开发&#xff0c;不管从薪资还是岗位数量…...

LeetCode·每日一题·1223.掷骰子模拟·记忆化搜索

作者&#xff1a;小迅链接&#xff1a;https://leetcode.cn/problems/dice-roll-simulation/solutions/2103471/ji-yi-hua-sou-suo-zhu-shi-chao-ji-xiang-xlfcs/来源&#xff1a;力扣&#xff08;LeetCode&#xff09;著作权归作者所有。商业转载请联系作者获得授权&#xff0…...

【GPLT 二阶题目集】L2-043 龙龙送外卖

参考地址&#xff1a;AcWing 4474. 龙龙送外卖&#xff08;杂题选讲&#xff09; 作者&#xff1a;yxc 感谢y总&#xff01; 龙龙是“饱了呀”外卖软件的注册骑手&#xff0c;负责送帕特小区的外卖。帕特小区的构造非常特别&#xff0c;都是双向道路且没有构成环 —— 你可以…...

Maven:基础知识

Maven概念图生命周期目录工程创建测试常用命令COMPILATION ERROR : 不再支持目标选项 5。请使用 7 或更高版本。问题解决pom.xml文件properties配置示例scope配置详解概念图 依赖管理构建项目Maven 的底层核心实现项目的构建和管理必须通过插件完成&#xff0c;但插件本身并不包…...

Web 框架 Flask 快速入门(一)flask基础与模板

前言 课程地址&#xff1a;Python Web 框架 Flask 快速入门 文章目录前言&#x1f334; Flask基础和模板&#x1f337; 一个简单的flask程序&#x1f33c; 模板的使用&#x1f334; Flask基础和模板 1、web框架的作用 避免重复造轮子&#xff0c;app程序不必关心于服务器的沟…...

1CN/Jaccard/PA/AA/RA/Katz/PageRank/SimRank

common neighbors&#xff08;CN&#xff09; 公共邻居的数量。 Jaccard 用于比较有限样本集之间的相似性与差异性。Jaccard系数值越大&#xff0c;样本相似度越高。 preferential attachment&#xff08;PA&#xff09; 节点倾向于连接到节点度较高的节点上&#xff0c;&…...

YOLOv5-Backbone模块实现

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f366; 参考文章地址&#xff1a; 365天深度学习训练营-第P8周&#xff1a;YOLOv5-Backbone模块实现&#x1f356; 作者&#xff1a;K同学啊一、前期准备1.设置GPUimport torch from torch impor…...

【C语言】程序环境和预处理

&#x1f307;个人主页&#xff1a;平凡的小苏 &#x1f4da;学习格言&#xff1a;别人可以拷贝我的模式&#xff0c;但不能拷贝我不断往前的激情 &#x1f6f8;C语言专栏&#xff1a;https://blog.csdn.net/vhhhbb/category_12174730.html 小苏希望大家能从这篇文章中收获到许…...

9.关系查询处理和查询优化

其他章节索引 梳理 名词解释 代数优化&#xff1a;是指关系代数表达式的优化&#xff0c;也即按照一定规则&#xff0c;通过对关系代数表达式进行等价变换&#xff0c;改变代数表达式中操作的次序和组合&#xff0c;使查询更高效物理优化&#xff1a;是指存取路径和底层操作算…...

计算机组成原理(三)

5.掌握定点数的表示和应用&#xff08;主要是无符号数和有符号数的表示、机器数的定点表示、数的机器码表示&#xff09;&#xff1b; 定点数&#xff1a;小数点位置固定不变。   定点小数&#xff1a;小数点固定在数值位与符号位之间&#xff1b;   定点整数&#xff1a;小…...

C. Least Prefix Sum codeforces每日一题

&#x1f680;前言 &#x1f680; 大家好啊&#xff0c;这里是幸麟 &#x1f9e9; 一名普通的大学牲&#xff0c;最近在学习算法 &#x1f9e9;每日一题的话难度的话是根据博主水平来找的 &#x1f9e9;所以可能难度比较低&#xff0c;以后会慢慢提高难度的 &#x1f9e9;此题标…...

ASEMI三相整流模块MDS100-16图片,MDS100-16尺寸

编辑-Z ASEMI三相整流模块MDS100-16参数&#xff1a; 型号&#xff1a;MDS100-16 最大重复峰值反向电压&#xff08;VRRM&#xff09;&#xff1a;1600V 最大RMS电桥输入电压&#xff08;VRMS&#xff09;&#xff1a;1700V 最大平均正向整流输出电流&#xff08;IF&#…...

【第37天】斐波那契数列与爬楼梯 | 迭代的鼻祖,递推与记忆化

本文已收录于专栏&#x1f338;《Java入门一百例》&#x1f338;学习指引序、专栏前言一、递推与记忆化二、【例题1】1、题目描述2、解题思路3、模板代码4、代码解析5.原题链接三、【例题1】1、题目描述2.解题思路3、模板代码4、代码解析5、原题链接三、推荐专栏四、课后习题序…...

Map集合

Map集合 Map接口的简介 Map用于保存具有映射关系的数据&#xff0c;Map里保存着两组数据&#xff1a;key和value&#xff0c;它们都可以使任何引用类型的数据&#xff0c;但key不能重复。所以通过指定的key就可以取出对应的value。 Map 没有继承 Collection 接口&#xff0c…...

PyQt5编程扩展 3.2 资源文件的使用

目录 本例运行效果&#xff1a; 设计Qt窗体 建立项目 放一个Group Box 放三个Label 放一个Horizontal Slider 放两个Line Edit 层次结构 布局 放一个Group Box 放两个Label 放两个Line Edit 放一个Push Button 层次结构 布局 放一个frame 层次结构 布局 窗体…...

Linux系统之文件共享目录设置方法

Linux系统之文件共享目录设置方法一、本次实践目的二、检查本地系统环境1.检查系统版本2.检查系统内核三、创建相关用户及用户组1.创建共享目录2.创建测试用户账号3.创建用户组4.设置用户的属组5.查看admin和IT用户组成员6.查看所有用户信息四、共享目录权限设置1.设置/data/so…...

上海亚商投顾:三大指数均涨超1% 芯片板块集体大涨

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。市场情绪三大指数今日低开高走&#xff0c;午后集体涨超1%&#xff0c;创业板指盘中涨超1.7%。芯片板块集体大涨&#xff0c;…...

Harbor私有仓库部署与管理

目录 前言 一、Harbor概述 二、Harbor 的特性 三、Harbor的构成 四、Harbor构建Docker私有仓库 1、环境配置 2、案例需求 3、部署Harbor服务 3.1、部署docker compose服务 3.2 下载或上传Harbor安装程序 3.3、启动Harbor 3.4、查看Harbor启动镜像 4、物理机访问se…...

互联网架构之 “高可用” 详解

一、什么是高可用 高可用HA&#xff08;High Availability&#xff09;是分布式系统架构设计中必须考虑的因素之一&#xff0c;它通常是指&#xff0c;通过设计减少系统不能提供服务的时间。 假设系统一直能够提供服务&#xff0c;我们说系统的可用性是100%。 如果系统每运行…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...