一个C++引用库的头文件预编译陷阱
写在前面
老胡最近在工作中,有个场景需要使用一个第三方库,引用头文件,链接库,编译运行,一切都很正常,但是接下来就遇到了一个很诡异的问题,调用该库的中的一个对象方法为对象修改属性的时候,会影响到对象的另外一个属性,当时百思不得其解,直呼灵异事件。
但后面静下心来细细看了一下代码和各种配置,发现了问题所在,现在把这个问题分享在这里,希望大家在以后的工作中如果遇到了类似的情况知道应该如何处理。
场景还原
库
当时引用的是一个第三方的静态链接库,场景非常简单,在项目中包含头文件,链接器指定路径和静态库名称,我们这里新建工程来生成一个非常简单的库。

其中,
//LibObject.h
#pragma once
struct LibObject
{
	int valueA{ 0 };
#ifdef AdditionalValue
	int valueB{ 0 };
#endif
	int valueC{ 0 };
	void DoSomething();
};
//LibObject.cpp
#include "LibObject.h"
void LibObject::DoSomething()
{
	valueA = 10;
#ifdef AdditionalValue
	valueB = 10;
#endif
}
简单至极,若预编译变量定义了AdditionalValue则定义多一个valueB并且在方法中赋值。编译库的时候我们指定AdditionalValue。
客户端代码

//main.cpp
#include "LibObject.h"
#include <iostream>
using namespace std;
int main()
{
	LibObject obj;
	cout << obj.valueA << endl;
	cout << obj.valueC << endl;
	obj.DoSomething();
	cout << obj.valueA << endl;
	cout << obj.valueC << endl;
	return 0;
}
客户端代码也很简单,声明一个对象,调用它的方法并在调用前后检查它的值,在编译客户端代码的时候,我们不定义AdditionalValue预编译变量。
运行试试
现在猜一猜输出是多少?

解惑
藏在背后的秘密
如果这个结果让你吃惊,那么相信我,你不是一个人,当时老胡也惊呆了,不管怎么看,DoSomething仅仅修改了ValueA,为什么会让ValueC的值变了?
秘密就在于编译库的时候和编译客户端代码的时候,我们使用了不同的预编译变量。
- 在客户端代码看来,LibObject是一个仅仅包含2个int类型的结构体,并且DoSomething方法会赋值给一个int,该int相对于this指针偏移是0。
 - 另一方面,在库代码看来,这个结构体包含了3个int类型变量,DoSomething会赋值给相对于this指针偏移为0和4的两个int。
 
所以答案揭晓了,为什么valueC的值会被影响,在于DoSomething执行的时候,相当于this指针偏移为4的int被赋值了,但是在我们从客户端代码构建的结构体中,这个位置存放的是valueC。

从这里可以看出,在方法执行的过程中,所谓的valueB其实内存地址和valueC是一样的。所以其实是那句给valueB赋值的语句把值给了valueC。
如何修复
知道了出问题的地方,修复起来就很简单了,一般来说两个办法。
- 如果第三方库能找到源代码,那我们可以重新用我们希望的预编译设置编译一次
 - 如果找不到源代码,那我们只有在客户端代码添加相应的预编译设置,确保和编译库时候所使用的一致
 
这两个办法都需要仔细阅读第三方库的文档。
 
希望本文能给遇到了类似问题的小伙伴一点启示,特别当你遇到了类似的情况的时候,这篇文章能够给你一些思路,毕竟,编译器甚至在这种情况下都不会给出任何警告,我们只能靠经验排查了。
一个C++引用库的头文件预编译陷阱的更多相关文章
- C++头文件预编译与命名空间使用方法
		
宏指令的预编译用法,用于多文件的头文件预编译判断 头文件代码: #include <iostream> #ifndef XB_H//预编译判断XB_H代码段是否被执行 #define XB ...
 - 用 #include “filename.h” 格式来引用非标准库的头文件
		
用 #include “filename.h” 格式来引用非标准库的头文件(编译器将 从用户的工作目录开始搜索) #include <iostream> /* run this progr ...
 - 用 #include <filename.h> 格式来引用标准库的头文件
		
用 #include <filename.h> 格式来引用标准库的头文件(编译器将从 标准库目录开始搜索). #include <iostream> /* run this p ...
 - 一点一点学写Makefile(3)-增加第三方库和头文件
		
我们在写代码的时候不一定都是有自己来完成,一个工程中会大量使用一些比较优秀的动态库.静态库等,我们在使用这些库完成所有的代码后,需要在编译的时候将这些库使用的头文件添加到我们的工程上,将他的库文件也添 ...
 - 《CMake实践》笔记三:构建静态库(.a) 与 动态库(.so) 及 如何使用外部共享库和头文件
		
<CMake实践>笔记一:PROJECT/MESSAGE/ADD_EXECUTABLE <CMake实践>笔记二:INSTALL/CMAKE_INSTALL_PREFIX &l ...
 - VS2013如何添加LIb库及头文件的步骤
		
在VS工程中,添加c/c++工程中外部头文件及库的基本步骤: 1.添加工程的头文件目录:工程---属性---配置属性---c/c++---常规---附加包含目录:加上头文件存放目录. 2.添加文件引用 ...
 - 静态资源打包:一个javescript 的src引用多个文件,一个link引用多个CSS文件
		
疑惑描述: 查看了淘宝网的首页源文件,看到这样的一个特殊的 <script src="http://a.tbcdn.cn/??s/kissy/1.1.6/kissy-min.js,p/ ...
 - linux 编译指定库、头文件的路径问题(转)
		
1. 为什么会出现undefined reference to 'xxxxx'错误? 首先这是链接错误,不是编译错误,也就是说如果只有这个错误,说明你的程序源码本身没有问题,是你用编译器编译时参数用得 ...
 - import第三方库的头文件找不到的错误
		
问题描述:使用cocoapods导入了第三方库,import该第三方库的某个头文件,然后编译报错找不到这个头文件内所import的头文件. 产生原因:我们需要配置头文件的搜索路径,告诉系统头文件的路径 ...
 
随机推荐
- Learning Web Performance with MDN
			
Learning Web Performance with MDN Web 性能是客观的衡量标准,是加载时间和运行时的感知用户体验. https://developer.mozilla.org/en- ...
 - TypeScript 3.7 RC & Nullish Coalescing
			
TypeScript 3.7 RC & Nullish Coalescing null, undefined default value https://devblogs.microsoft. ...
 - py conda 换源
			
安装软件 $ conda install wxpython 如果出现http错误,安装OpenSSL $ conda config --add channels https://mirrors.tun ...
 - 10月上线的NGK global有怎样的发展前景?
			
随着NGK global 10月份的上线时间将近,社区中也开始纷纷讨论.预测起NGK global上线后的表现,对此小编也有一些自己的理解,就此分享给大家. 在基于实体生态的赋能下,NGK globa ...
 - 云原生系列6 基于springcloud架构风格的本地debug实现
			
debug是程序员在日常开发中最常使用的操作, 那么,你是如何快速在微服务架构风格下快速debug后端服务呢? 开发现状 开发的理想状态 本地调测的使用步骤 登录智能网关 如果集成开发环境是在本地局域 ...
 - django学习-1.开始hello world!
			
1.前言 当你想走上测试开发之路,用python开发出一个web页面的时候,需要找一个支持python语言的web框架.django框架有丰富的文档和学习资料,也是非常成熟的web开发框架,想学pyt ...
 - [转]ROS Q&A | How to read LaserScan data
			
http://www.theconstructsim.com/read-laserscan-data/ Step 1. Open a project on ROS Development Studio ...
 - 使用RSEM进行转录组测序的差异表达分析
			
仍然是两年前的笔记 1. prepare-reference 如果用RSEM对比对后的bam进行转录本定量,则在比对过程中要确保比对用到的索引是由rsem-prepare-reference产生的. ...
 - BigDecimalUtil:对double类型的数据进行处理(加减乘除、四舍五入、类型转换、比较两个值的大小)
			
该工具类所在的包:import java.math.BigDecimal; 项目中使用该工具类的相关代码: // 如果Output表中已经存在该节点(插入数据的节点)的信息,则修改Output表中的这 ...
 - 腾讯数据库tdsql部署与验证
			
环境准备 | 主机 | IP | 配置(最低要求配置) | | :----- | ------------- | ------------------ | | node-1 | 192.168.1.8 ...