小心C++利用#define定义全局变量的天坑,其不服从条件编译的限制

项目结构如下:

project
	--common.h
		#pragma once
		#define qq(mm) \
			int mm_x;
		qq(haha)
	--aa.cpp 
		#include <common.h>
		int main()
		{
		    return 0;
		}
	--bb.cpp
		#include <common.h>
	--CMakeLists.txt
		add_library(test SHARED ./aa.cpp ./bb.cpp)
		(或者用gcc指令: g++ aa.cpp bb.cpp -o main.exe)

这里宏变量qq就是定义了一个全局整形变量mm_x,
比如在common.h中写一行qq(haha), 预编译时就会被替换成int haha_x;也就是说在头文件中定义了一个全局整形变量haha_x

现在执行cmakelists进行编译,首先回顾一下编译的过程:

每个cpp文件都是一个独立的编译单元,会被编译成一个目标文件,
此处将两个cpp文件共同编成一个动态库,其实是先编译aa.cpp生成test.obj, 再编译bb.cpp, 生成bb.obj, 然后把bb.obj往test.obj上链接,

但是在链接时,会报错multiple definition of 'haha_x’

回顾一下头文件,不是加了pragma once吗,这个头文件内容只会被编译1次,从第二次被包含开始,其内容就不会再被编译了,只会被bb.cpp的源码部分拿来认一认一些声明

那么这里利用宏定义了一个全局变量(严格来说是声明了一个全局变量), 也应该服从pragma once的条件编译规则呀, 第一次被包含,真的声明了haha_x这样一个变量, 但是从第二次开始就不应该再在bb.cpp这个编译单元中再声明一次了,为何链接时还会报重复定义的错误呢?

难道是pragma once有啥问题吗,换#ifndef COMMON_H试试? 但是会发现也无济于事, 依然报重复定义的错

好吧,其实原因是:预编译有三个步骤, 递归包含头文件→宏替换→条件编译, 也即qq(haha)被替换成int haha_x;是优先于条件编译的, 宏定义产生的全局变量,其会直接加入到源码,不受条件编译的限制,

所以在aa.cpp中第一次包含common.h,会声明一个haha_x变量, 在bb.cpp中第二次包含common.h, 也会直接声明一个haha_x,再去看条件编译的内容, 然后两个目标以合并,完了, haha_x这个名字冲突了!
但是如果把使用宏的qq(haha)从函数与类外部移动到函数或类内部, 就可以避免这个问题, 也即其不再是全局变量,自然就不会冲突了

所以,要么别把宏定义的全局变量放在头文件中声明,要么这个头文件只允许被包含一次

采取后者的方法,要求这个头文件真的没啥共用的内容,不被多次包含也是可以的,否则就会导致别人源码不认识一些声明. 那么还不如干脆将其变成一个源文件. 也即

project
	--common.h
		#pragma once
		#define qq(mm) \
			int mm_x;
	--defines.cpp
		#include <common.h>
		qq(haha)
	--aa.cpp 
		#include <common.h>
		int main()
		{
		    return 0;
		}
	--bb.cpp
		#include <common.h>
	--CMakeLists.txt
		add_library(test SHARED ./aa.cpp ./bb.cpp ./defines.cpp)
		(或者用gcc指令: g++ aa.cpp bb.cpp defines.cpp -o main.exe)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值