近期在学习C++,配合大牛书Primer看的效果还是很不错的。学到指针时又遇到了extern的使用,但自己回忆起来,好像在之前学过的内容里并没有对extern有非常深刻的印象。

 

于是结合着书中的内容和自己查阅的博客,我写出了以下代码进行测试。

IDE版本:Visual Studio 2022 Community

// ------test1.cpp------
#include <iostream>
using namespace std;

extern int i;
extern int print1();

int main()
{
    print1();
    return 0;
}
// ------test2.cpp------
#include <iostream>
using namespace std;

int i;

int print1() {
    i = 1;
    cout << i << endl;
    return 0;
}

注:test1.cpp与test2.cpp处于同一解决方案的同一文件夹下(文件夹名:源文件)

 

 

 

此时运行test1.cpp,可以看到正常的输出结果:

 

 

 

 浏览了博客《C++中extern关键字使用》后得知,通过引入头文件、在头文件中定义的方式,可以达到与extern一样的引用其他文件中变量的效果,于是尝试:

// ------test1.cpp------
#include <iostream>
#include "test11.h"
using namespace std;

//extern int i;
//extern int print1();

int main()
{
    print2();
    return 0;
}
// ------test11.h------
#pragma once
#include <iostream>
using namespace std;
int i1 = 1002;

int print2() {
    cout << ++i1 << endl;
    return 0;
}

注:test1.cpp目录无变化,test11.h文件在同一解决方案内的“头文件”目录里

 

 

 

此时运行test1.cpp,同样能看到正常的输出结果

 

 

 

到此,定义在其它文件中的函数和变量,可以使用的两种方法已经试验完成了:

        一、使用头文件调用,这时候,函数和变量必须在头文件中定义和声明。

        二、使用extern关键字调用,这时候函数和变量在.cpp或者.c文件中定义和声明。

 

但此时大聪明的我,又把这两种方式揉到了一起(其实是没区分开),于是有了如下的代码:

// ------test1.cpp------
#include <iostream>
#include "test2.cpp" using namespace std; //extern int i; //extern int print1(); int main() { print1(); return 0; }
// ------test2.cpp------
// 该文件内容无变化,此处仅为方便阅览
#include <iostream>
using namespace std;

int i;

int print1() {
    i = 1;
    cout << i << endl;
    return 0;
}

我在test1.cpp中引入了test2.cpp,随后在main函数中直接调用了 print1 函数。此时链接器报错:

 

 test1中已经定义了 i 变量和 print1 函数,出现了多重定义。正在我好奇为什么将头文件引用换成源文件引用就会引发重复定义时,我在上述博客中看到了如下的一句话:

如果一个工程现编译cpp文件,在把多个目标文件链接成为可执行文件,而两个或多个文件中,定义了相同的全局变量,那么,程序编译的时候不会报错,因为编译器单独编译每个文件,在链接可执行文件的时候,由于多个目标文件中含有相同的全局变量,而生成可执行文件的时候,任何文件中定义的全局变量对其它目标文件都是可见的,此时由于变量定义冲突而发生错误。

 

原博客中给出的代码为在两个.cpp文件中均定义 i 变量和 print 函数,与我代码的写法不同,但报错内容是相同的。

而博客中紧接着的一段话给了我答案:

PS:定义在.h文件中的函数和变量不能使用extern变量声明,原因是#include <filename>在预编译的时候将.h文件中的内容插入了cpp文件中,因此编译器找得到在其它.h文件中定义的变量或者函数。编译的时候,只编译cpp文件的内容,.h文件时不参与编译,如果使用extern声明在.h文件中定义的变量或者函数,那么声明为extern的变量和函数在其它.cpp文件中找不到,因此程序编译的时候就发生了错误。

 

在预编译时,#include "test2.cpp" 指令已经将test2.cpp文件中的内容插入到了test1.cpp文件中,因此在test1编译时已经对 i 变量和 print1 函数进行了定义;随后编译器再编译test2.cpp,自然会引发全局变量定义冲突的错误(因为test2里的内容已经作为test1的一部分编译过了,此时是内容的第二次编译)。

 

除了我上面列出的错误写法之外,上面的话也列出了另一种非法的extern使用:

// ------test1.cpp------
#include <iostream>
using namespace std;

extern int i;
extern int print2();

int main()
{
    print2();
    return 0;
}
// ------test11.h------
//文件内容无变化,此处仅为方便阅览
#pragma once
#include <iostream>
using namespace std;
int i1 = 1002;

int print2() {
    cout << ++i1 << endl;
    return 0;
}

此时链接器报错:

 

 

虽然test1.cpp中对 i 变量和 print2 函数声明了extern,但由于没有任何.cpp文件中对该两项内容进行定义,因此链接器无法找到这两项的实际定义的位置。

 

总结:C++中,关于引用其他文件中变量和函数的方法共有两种:

1. 在文件中插入.h头文件,使用头文件中定义的变量和函数;

2. 使用extern声明在其他.cpp文件中定义好的变量和函数(此时变量和函数通常为全局的)

 

本文也列出了两种错误的使用方法,请注意不要将其与正确用法混淆。