想象一下这个工作场景:你在为一个项目写一个功能库,别人调用库中提供的函数,后来你发现库里的函数A是多余的。
具有完美情节的你,就是想把这个函数A废弃掉,此时肯定是不能直接删掉,因为你不知道别人在多少个地方调用了这个函数。
这种情况如何处理比较好呢?
这篇小短文就来聊一聊这个问题。
测试文件只有 3 个:api.h, api.c 和 main.c
api.h 文件内容:声明了 2 个函数。
api.c 文件内容:定义了 2 个函数。
编译得到库文件 libapi.so。编译指令:
- gcc -fPIC -shared api.c -o libapi.so
main.c 文件内容:
编译得到可执行文件:
- gcc main.c -o main -L./ -Wl,-rpath=./ -lapi
以上代码的简单程度,等价于 helloworld 了。
现在,你觉得 init 这个函数是多余的,想把它去掉,可以这么来修改。
api.c 文件中,把 init() 函数删除掉。
api.h 文件内容改为如下:
关键代码是这一行:
- #define init() (1) API_DEPRECATED
既然 api.c 文件已经把这个函数删除了,但是 main.c 文件中又调用了这个函数,因此以宏定义的形式提供 init 这个符号。
也就是说:
在第一个版本中,main.c 文件中的 init 是一个函数,被编译器处理,在链接阶段从 libapi.so 库中找到这个函数的地址;
在第二个版本中,init 被定义成宏,在预处理阶段被替换成后面的 (1) API_DEPRECATED。
在编译可执行文件时,编译器输出下面的这段话:
- gcc main.c -o main -L./ -Wl,-rpath=./ -lapi
这样就达到了最初的目的!也就是提示使用者:这个函数已经被废弃了,最好别用它!
_Pragma 类似于 Microsoft 特定的 __pragma 关键字,只不过它是标准的一部分。它是在 C99 中为 C 引入的。对于 c + +,它是在 c + + 11 中引入的。它允许将指令放入宏定义中。
在头文件中,为了防止被重复包含,一般有 3 种处理方式:
(1) 第一种处理方式:
- #ifdef MY_API
- #define MY_API
- // 头文件内容
- #endif
(2) 第二种处理方式
- #pragma once
- // 头文件内容
以上这 2 种方式都可以防止同一个头文件被重复包含,但是还是有一些区别的。
第一种方式:预处理器还是需要去搜寻文件,然后打开文件,读取文件的内容之后,检查 MY_API 是否已经被定义过。
第二种方式:能加快编译速度,因为这是一种高端的机制;编译器会自动比对文件名,而不需要在头文件去判断 #ifndef 和 #endif,这样就省去了中间的搜寻、打开和读取操作。
(3) 第三种处理方式
- _Pragma("once")
这种方式与第二种方式的区别是:
#pragma 是编译器的扩展,也就是说它是由编译器来决定的,也许编译器A支持,但是编译器B就不一定支持了,虽然这种可能性比较小。
_Pragma 操作符是语言层面的标准,既然是标准,那么编译器就必须要遵循标准,所以也推荐使用这种方式。
记得侯杰老师在 C++ 的视频课程中说到:我们写代码,不仅仅要保证功能上的正确,而且要把代码写的很大气!我感觉用 _Pragma 可能比 #ifndef 更大气一些。
- #pragma message("the #pragma way")
- _Pragma ("message( \"the _Pragma way\")")
上面两行的内容输出信息是一样的,需要注意的是嵌套的双引号需要用反斜线去转义。
“智慧校园”建设着眼于未来校园IT规划 无论是教学、科研 还是决策、管理 都可以...
根据惯例从三月份开始,各大云服务商甚至电商购物平台都会有一些促销活动。一来...
1.如果有钱也是一种错,那我情愿一错再错。 2.老师,既然您用作业让我们熬夜,...
简介 正则表达式是我们做数据匹配的时候常用的一种工具 虽然正则表达式的语法并...
大数据分析工具BI,是企业数据化管理的一整套方案,用于将企业中现有的数据进行...
本文通过系统性地介绍阿里巴巴 AI 中台的技术质量体系搜索推荐广告应用的质量是...
目录 一、分支规约 二、版本号规约 2.1 主版本号 首位版本号 2.2 次版本号 迭代...
1.小学上了十年,中学十二年,我被评为全校最熟悉的面孔,新老师来了都跟我打听...
对于现代软件研发来说 持续、快速、高质量、低风险地交付需求特性 是业务对研发...
图片来源:https://pixabay.com/images/id-1332343/ 过去的一年教会了我们许多关...