#pragma once的作用以及全局变量的问题

提出问题

第一次遇到#pragma once的时候,到网上找了些资料,大部分答案都大同小异,大概意思是让编译器只编译一次。我们都知道,一般不在头文件中定义全局变量,因为那会导致该变量多次定义。那么问题来了,既然用#pragma once预编译命令可以防止重复编译,为什么不能在头文件中定义全局变量呢?这个问题长期困扰着我,平时也就是记住这些规则而已。正所谓要知其所以然嘛,那我们就来探讨一下这个问题。

#pragma once的作用

我们从三个文件开始。
第一个文件是test1.h

//#pragma once
//test1.h

struct test1
{
	int val;
};

第二个文件是test2.h

#pragma once
//test2.h

#include "test1.h"

最后一个文件test.cpp

// test.cpp

#include "stdafx.h"		//我用的是vs2015,其他编译器可以不用包含这个头文件.
#include "test1.h"
#include "test2.h"

int main()
{
    return 0;
}

这种情况是可能的,因为我们可能会同时在源文件中使用这两个头文件的内容。
下面我们来编译一下:
编译结果
为什么会报错呢?一个源文件包含一个头文件,只是以头文件的形式展开,就是把头文件里的内容copy到源文件中去。在test.cpp文件中,#include "test2.h"展开后,会包含一个test1.h,而原本就包含了一个test1.h。最终结果就是结构体test1被定义了两次,然后报错了。
那么我把test1.h的注释(//#pragma once)去掉会怎么样?答案是yes!编译通过。#pragma once的作用是对于某一个文件,如果多次包含另一个文件的话,只编译一次。

头文件定义全局变量

我们继续创建3个文件。
第一个文件为test1.h

#pragma once
//test1.h

int a = 0;

第二个文件为test1.cpp

//test1.cpp
#include "stdafx.h"
#include "test1.h"

第三个文件为test.cpp
// test.cpp

#include "stdafx.h"		//我用的是vs2015,其他编译器可以不用包含这个头文件.
#include "test1.h"

int main()
{
    return 0;
}

编译看下会发生什么。
编译结果
oh no!不是说好的用#pragma once只编译一次吗,怎么又重定义了呢?宝贝你居然骗我。我们仔细观察错误提示发现,LNKxxx之类的。这是说明在编译的过程中,链接出错了。(?a@@3HA)是编译器对全局变量a生成的一个全局符号,这个符号的作用相当于链接器识别的一个id。
而test1.cpp和test.cpp两个源文件都包含了test1.h。因此int a = 0;会被编译器展开到那两个源文件中,因此,也会生成两个符号。在链接的过程中,这两个符号重复了,所以就报错了。
如果有同学对编译和链接的过程不是很明白的,可以参考深入理解计算机系统的第七章,那里讲的很透彻。

全局变量的解决方案

针对上一部分的情况,那么我想要在test1.cpp和test.cpp两个源文件中使用全局变量a怎么办?
我们修改这3个文件
test1.h:

#pragma once
//test1.h

extern int a;

int func();

test.cpp:

// test.cpp

#include "stdafx.h"		//我用的是vs2015,其他编译器可以不用包含这个头文件.
#include "test1.h"

int a = 0;

int main()
{
	a = 1;
	printf("%d\n", a);

	func();
	printf("%d\n", a);
    return 0;
}

test1.cpp:

//test1.cpp
#include "stdafx.h"
#include "test1.h"

int func() {
	a = 2;
	return a;
}

在test1.h中,使用extern表示变量a是外来的,在这里只是声明它,并没有定义。这样也就不会生成两个全局符号。
以下是运行结果,可以看到test1.cpp中的func函数成功地修改了变量a的值。
在这里插入图片描述

总结

#pragma once预编译指令防止重复编译是针对某一个源文件而言的,即某个源文件包含了两个头文件,而那两个头文件之一又包含了另一个,此时#pragma once会发生作用,起到只编译一次的作用。
全局变量不要定义在头文件中则适用于多个源文件的。多个源文件同时包含某一个头文件,那个头文件又定义了一个全局变量。此时会发生链接错误,解决方法是使用extern。
这些细节很容易被我们忽略掉,从而引起混淆。写篇文章mark一下。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值