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的三个冷知识
- 使用 # ,可以把一个宏参数变成对应的字符串
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" }
- ##可以把位于它两边的符号合成一个符号。
它允许宏定义从分离的文本片段创建标识符(标识符必须预先定义好)。#define ADD_TO_SUM(num, value) \ sum##num += value;int main () {int sum5 =10;ADD_TO_SUM(5, 10); //预处理将该语句替换为 sum5 += 10 }
- 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
三. 条件编译
常量表达式条件编译:
//单分支条件编译指令 #if 常量表达式//代码段#endif
//多分支条件编译指令 #if 常量表达式//代码段#elif 常量表达式//代码段#else//代码段#endif//#elif 可以类比 else if 来理解
当常量表达式为真则对#if和#endif(或#elif,#else)之间的代码段执行编译,为假则编译器会自动屏蔽掉#if和#endif之间的代码段(或#elif,#else)(常量表达式由预处理器求值)
#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 附:关于#define的三个冷知识 三. 条件…...
2023互联网相关岗位转行与就业选择的简单分析
文章目录1、城市2、岗位1、城市 能找得到工作的城市,可能主要也就这些base了 2、岗位 主要技术岗位 Python 侧重人工智能,人工智能门槛高大家心知肚明。如果学python 不走人工智能,只走单纯的后端开发,不管从薪资还是岗位数量…...
LeetCode·每日一题·1223.掷骰子模拟·记忆化搜索
作者:小迅链接:https://leetcode.cn/problems/dice-roll-simulation/solutions/2103471/ji-yi-hua-sou-suo-zhu-shi-chao-ji-xiang-xlfcs/来源:力扣(LeetCode)著作权归作者所有。商业转载请联系作者获得授权࿰…...
【GPLT 二阶题目集】L2-043 龙龙送外卖
参考地址:AcWing 4474. 龙龙送外卖(杂题选讲) 作者:yxc 感谢y总! 龙龙是“饱了呀”外卖软件的注册骑手,负责送帕特小区的外卖。帕特小区的构造非常特别,都是双向道路且没有构成环 —— 你可以…...
Maven:基础知识
Maven概念图生命周期目录工程创建测试常用命令COMPILATION ERROR : 不再支持目标选项 5。请使用 7 或更高版本。问题解决pom.xml文件properties配置示例scope配置详解概念图 依赖管理构建项目Maven 的底层核心实现项目的构建和管理必须通过插件完成,但插件本身并不包…...
Web 框架 Flask 快速入门(一)flask基础与模板
前言 课程地址:Python Web 框架 Flask 快速入门 文章目录前言🌴 Flask基础和模板🌷 一个简单的flask程序🌼 模板的使用🌴 Flask基础和模板 1、web框架的作用 避免重复造轮子,app程序不必关心于服务器的沟…...
1CN/Jaccard/PA/AA/RA/Katz/PageRank/SimRank
common neighbors(CN) 公共邻居的数量。 Jaccard 用于比较有限样本集之间的相似性与差异性。Jaccard系数值越大,样本相似度越高。 preferential attachment(PA) 节点倾向于连接到节点度较高的节点上,&…...
YOLOv5-Backbone模块实现
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍦 参考文章地址: 365天深度学习训练营-第P8周:YOLOv5-Backbone模块实现🍖 作者:K同学啊一、前期准备1.设置GPUimport torch from torch impor…...
【C语言】程序环境和预处理
🌇个人主页:平凡的小苏 📚学习格言:别人可以拷贝我的模式,但不能拷贝我不断往前的激情 🛸C语言专栏:https://blog.csdn.net/vhhhbb/category_12174730.html 小苏希望大家能从这篇文章中收获到许…...
9.关系查询处理和查询优化
其他章节索引 梳理 名词解释 代数优化:是指关系代数表达式的优化,也即按照一定规则,通过对关系代数表达式进行等价变换,改变代数表达式中操作的次序和组合,使查询更高效物理优化:是指存取路径和底层操作算…...
计算机组成原理(三)
5.掌握定点数的表示和应用(主要是无符号数和有符号数的表示、机器数的定点表示、数的机器码表示); 定点数:小数点位置固定不变。 定点小数:小数点固定在数值位与符号位之间; 定点整数:小…...
C. Least Prefix Sum codeforces每日一题
🚀前言 🚀 大家好啊,这里是幸麟 🧩 一名普通的大学牲,最近在学习算法 🧩每日一题的话难度的话是根据博主水平来找的 🧩所以可能难度比较低,以后会慢慢提高难度的 🧩此题标…...
ASEMI三相整流模块MDS100-16图片,MDS100-16尺寸
编辑-Z ASEMI三相整流模块MDS100-16参数: 型号:MDS100-16 最大重复峰值反向电压(VRRM):1600V 最大RMS电桥输入电压(VRMS):1700V 最大平均正向整流输出电流(IF&#…...
【第37天】斐波那契数列与爬楼梯 | 迭代的鼻祖,递推与记忆化
本文已收录于专栏🌸《Java入门一百例》🌸学习指引序、专栏前言一、递推与记忆化二、【例题1】1、题目描述2、解题思路3、模板代码4、代码解析5.原题链接三、【例题1】1、题目描述2.解题思路3、模板代码4、代码解析5、原题链接三、推荐专栏四、课后习题序…...
Map集合
Map集合 Map接口的简介 Map用于保存具有映射关系的数据,Map里保存着两组数据:key和value,它们都可以使任何引用类型的数据,但key不能重复。所以通过指定的key就可以取出对应的value。 Map 没有继承 Collection 接口,…...
PyQt5编程扩展 3.2 资源文件的使用
目录 本例运行效果: 设计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% 芯片板块集体大涨
上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。市场情绪三大指数今日低开高走,午后集体涨超1%,创业板指盘中涨超1.7%。芯片板块集体大涨,…...
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(High Availability)是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间。 假设系统一直能够提供服务,我们说系统的可用性是100%。 如果系统每运行…...
分布式高级篇4 —— 商城业务(2)
一、订单服务1、订单基本概念2、订单基本构成3、订单状态4、订单流程5、配置拦截器拦截订单请求6、订单确认页模型抽取7、订单确认页vo封装8、Feign 远程调用请求头丢失问题\*\*\*\*\* 惨痛教训9、Feign 异步调用请求头丢失问题10、查看库存状态11、模拟计算运费12、接口幂等性…...
二分查找基本原理
二分查找基本原理1.二分查找1.1 基本概念1.2 二分查找查找步骤1.2.1 中间索引不能整除,取整数作为中间索引1.2.2 索引不能整除,整数1作为中间索引1.3 二分查找大O记法表示2. 二分查找代码实现1.二分查找 1.1 基本概念 二分法(折半查找)是一…...
【Python实战案例】Python3网络爬虫:“可惜你不看火影,也不明白这个视频的分量......”m3u8视频下载,那些事儿~
前言 哈喽!上午好嘞,各位小可爱们!有没有等着急了呀~ 由于最近一直在学习新的内容,所以耽搁了一下下,抱歉.jpg 双手合十。 所有文章完整的素材源码都在👇👇 粉丝白嫖源码福利,请移…...
UE4:使用样条生成随机路径,并使物体沿着路径行走
一、关于样条的相关知识 参考自:样条函数 - 馒头and花卷 - 博客园 三次样条(cubic spline)插值 - 知乎 B-Spline(三)样条曲线的性质 - Fun With GeometryFun With Geometry 个人理解的也不是非常深,但是大概要知道的就是样条具…...
计算机组成原理(判断题)
计算机控制器是根据事先编好的程序,根据其指令来进行控制只会每一步骤的操作; 面向主存的双总线结构计算机系统,因在CPU与主存之间增加了一组存储器总线,由于通过存储器总线访存,提高了CPU的访存速度,也减轻…...
error: failed to push some refs to ... 就这篇,一定帮你解决
目录 一、问题产生原因 二、解决办法 三、如果还是出问题,怎么办?(必杀) 一、问题产生原因 当你直接在github上在线修改了代码,或者是直接向某个库中添加文件,但是没有对本地库同步,接着你想…...
DAMA数据管理知识体系指南之数据仓库和商务智能管理
第9章 数据仓库和商务智能管理 9.1简介 数据仓库(Data Warehouse,DW)由两个主要部分构成:首先是一个整合的决策支持数据库,其次是用于收集、清洗、转换、存储来自于各种操作型数据源和外部数据源数据的相关软件程序。两者结合以支持历史的、…...
PHP的五种常见设计模式
工厂模式 最初在设计模式 一书中,许多设计模式都鼓励使用松散耦合。要理解这个概念,让我们最好谈一下许多开发人员从事大型系统的艰苦历程。在更改一个代码片段时,就会发生问题,系统其他部分 —— 您曾认为完全不相关的部分中也有…...
教你搞懂线段树,从基础到提高
秋名山码民的主页 🎉欢迎关注🔎点赞👍收藏⭐️留言📝 🙏作者水平有限,如发现错误,还请私信或者评论区留言! 目录前言线段树逻辑概念线段树的俩个重要用处代码实现线段树题目巩固最后…...
C语言进阶——自定义类型:结构体
🌇个人主页:_麦麦_ 📚今日名言:生活不可能像你想象的那么好,也不会像你想象的那么糟。——莫泊桑《羊脂球》 目录 一、前言 二、正文 1结构体 1.1结构体的基础知识 1.2结构的声明 1.3特殊的声明 1.4结构体变量的…...
iis7添加php网站/灰色词排名代做
一 组合概念 一个类的对象作为另外一个类对象的属性第一个例子: 2 class Weapon:3 def prick(self, obj): # 这是该装备的主动技能,扎死对方4 obj.life_value - 500 # 假设攻击力是5005 6 class Person: # 定义一个人类7 role person # 人的角…...
wordpress knowhow/百度经验手机版官网
关于科学的作文600字(精选11篇)在日复一日的学习、工作或生活中,大家都经常接触到作文吧,作文要求篇章结构完整,一定要避免无结尾作文的出现。那么一般作文是怎么写的呢?以下是小编帮大家整理的关于科学的作文600字(精选11篇)&…...
分析苏宁易购的网站建设/网站排名查询工具有哪些
~~~题面~~~ 题解: 这是一道强题emmmm,做法非常巧妙,,,我也是看了好久大佬题解才看明白一点 首先考虑没有限制的情况,即n个老鼠可以在同…...
做视频网站用什么源码/兰州网络推广技术
属性 类型 默认值 autoOpen Boolean true 实例化时是否自动显示对话框。设置为 false 时,使用 open 方法显示对话框。 false 代码示例 创建实例时设置属性值 $(".class").dialog({…...
完善网站的建设工作流程/比较成功的网络营销案例
我前几天随手画了一张图:在2000年初,我和朋友就在聊:手机肯定会变成计算机的。不过智能手机时代真的来了,我们也没干啥。(1)PC单机:中国1990-1995从1977年Apple发明个人电脑开始,单机…...
高端手机网站定制/中小企业网络推广
#include<stdio.h> #include<math.h> #include<string.h> //其他任意进制转换为十进制 int main() { int trans(char a[],int ); char strupr(char ); char arr[100]; int t; printf("请输入进制的类型:"); scanf("%d",&t); printf(…...