How variable/function is found during build 编译器如何找到变量和函数

本文源自于我试图理解”extern”所做的实验。本文不讨论extern “C”的使用情况。
先看实验,再讲感想:

变量实验(1):普遍易懂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//header.h
int a=999;

//main.cpp
#include <iostream>
#include "header.h"
using namespace std;
int main(){
cout << "a=" << a << endl;
return 0;
}

//结果如下
$ g++ -c main.cpp && g++ main.o -o main
$ ./main
a=999

变量实验(1.1):只是去掉了a的初始化,发现它会默认初始化为0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//header.h
int a;

//main.cpp
#include <iostream>
#include "header.h"
using namespace std;
int main(){
cout << "a=" << a << endl;
return 0;
}

//结果如下
$ g++ -c main.cpp && g++ main.o -o main
$ ./main
a=0

变量实验(2):引入test.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//header.h
int a;

//test.cpp
int a = 888;

//main.cpp
#include <iostream>
#include "header.h"
using namespace std;
int main(){
cout << "a=" << a << endl;
return 0;
}

//结果如下
$ g++ -c main.cpp && g++ -c test.cpp && g++ main.o test.o -o main
test.o:(.data+0x0): multiple definition of `a'
main.o:(.bss+0x0): first defined here
collect2: error: ld returned 1 exit status

报错说在链接阶段发现test.o和main.o重复定义了变量a,因此链接失败

变量实验(3):在test.cpp使用extern来指示外部变量a,而非重新定义变量a

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//header.h
int a;

//test.cpp
extern int a = 888;

//main.cpp
#include <iostream>
#include "header.h"
using namespace std;

int main(){
cout << "a=" << a << endl;
return 0;
}

//结果如下
$ g++ -c main.cpp && g++ -c test.cpp && g++ main.o test.o -o main
test.cpp:4:12: warning: ‘a’ initialized and declared ‘extern’
extern int a=888;
^
test.o:(.data+0x0): multiple definition of `a'
main.o:(.bss+0x0): first defined here
collect2: error: ld returned 1 exit status

报错说的意思是test.cpp对a进行了初始化,所以extern关键字被忽略,所以仍然是重复定义了a

变量实验(4):那就不要初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//header.h
int a;

//test.cpp
extern int a;

//main.cpp
#include <iostream>
#include "header.h"
using namespace std;

int main(){
cout << "a=" << a << endl;
return 0;
}

//结果如下
$ g++ -c main.cpp && g++ -c test.cpp && g++ main.o test.o -o main
./main
a=0

成功

变量实验(5):反过来,试一下header.h中extern,而test.cpp中定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//header.h
extern int a;

//test.cpp
int a;

//main.cpp
#include <iostream>
#include "header.h"
using namespace std;

int main(){
cout << "a=" << a << endl;
return 0;
}

//结果如下
$ g++ -c main.cpp && g++ -c test.cpp && g++ main.o test.o -o main
./main
a=0

成功

变量试验完了,下面依葫芦画瓢,开始试验函数

函数实验(1):通俗易懂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//header.h
#include <iostream>
using namespace std;
void func() {cout<< "This is func() from header.h"<<endl;}

//main.cpp
#include <iostream>
#include "header.h"
using namespace std;
int main(){
func();
return 0;
}

//结果如下
$ g++ -c main.cpp && g++ main.o -o main
./main
This is func() from header.h

函数实验(2):引入test.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//header.h
#include <iostream>
using namespace std;
void func() {cout<< "This is func() from header.h"<<endl;}

//test.cpp
#include <iostream>
using namespace std;
void func() {cout<< "This is func() from test.cpp"<<endl;}

//main.cpp
#include <iostream>
#include "header.h"
using namespace std;
int main(){
func();
return 0;
}

//结果如下
$ g++ -c main.cpp && g++ -c test.cpp && g++ main.o test.o -o main
test.o: In function `func()':
test.cpp:(.text+0x0): multiple definition of `func()'
main.o:main.cpp:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status

报错说在链接阶段发现test.o和main.o重复定义了函数func(),因此链接失败

函数实验(3):在test.cpp使用extern来指示外部函数func(),而非重新定义函数func()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//header.h
#include <iostream>
using namespace std;
void func() {cout<< "This is func() from header.h"<<endl;}

//test.cpp
#include <iostream>
using namespace std;
extern void func() {cout<< "This is func() from test.cpp"<<endl;}

//main.cpp
#include <iostream>
#include "header.h"
using namespace std;
int main(){
func();
return 0;
}

//结果如下
$ g++ -c main.cpp && g++ -c test.cpp && g++ main.o test.o -o main
test.o: In function `func()':
test.cpp:(.text+0x0): multiple definition of `func()'
main.o:main.cpp:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status

报错说的意思是test.cpp还是对func()进行了实现,extern实际上没起作用,所以还是重新定义了

函数实验(4):那就不要实现函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//header.h
#include <iostream>
using namespace std;
void func() {cout<< "This is func() from header.h"<<endl;}

//test.cpp
#include <iostream>
using namespace std;
extern void func();

//main.cpp
#include <iostream>
#include "header.h"
using namespace std;
int main(){
func();
return 0;
}

//结果如下
$ g++ -c main.cpp && g++ -c test.cpp && g++ main.o test.o -o main
$ ./main
This is func() from header.h

成功

函数实验(5):反过来,试一下header.h中extern,而test.cpp中定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//header.h
#include <iostream>
using namespace std;
extern void func();

//test.cpp
#include <iostream>
using namespace std;
void func() {cout<< "This is func() from test.cpp"<<endl;};

//main.cpp
#include <iostream>
#include "header.h"
using namespace std;
int main(){
func();
return 0;
}

//结果如下
$ g++ -c main.cpp && g++ -c test.cpp && g++ main.o test.o -o main
$ /main
This is func() from test.cpp

成功

额外函数实验(1):header.h中的extern也省略掉

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//header.h
#include <iostream>
using namespace std;
void func();

//test.cpp
#include <iostream>
using namespace std;
void func() {cout<< "This is func() from test.cpp"<<endl;};

//main.cpp
#include <iostream>
#include "header.h"
using namespace std;
int main(){
func();
return 0;
}

//结果如下
$ g++ -c main.cpp && g++ -c test.cpp && g++ main.o test.o -o main
$ /main
This is func() from test.cpp

额外函数实验(2):test.cpp中的extern也省略掉

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//header.h
#include <iostream>
using namespace std;
void func() {cout<< "This is func() from header.h"<<endl;}

//test.cpp
#include <iostream>
using namespace std;
void func();

//main.cpp
#include <iostream>
#include "header.h"
using namespace std;
int main(){
func();
return 0;
}

//结果如下
$ g++ -c main.cpp && g++ -c test.cpp && g++ main.o test.o -o main
$ ./main
This is func() from header.h

成功

额外变量实验:header.h中的extern也省略掉

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//header.h
int a;

//test.cpp
int a = 888;

//main.cpp
#include <iostream>
#include "header.h"
using namespace std;
int main(){
cout << "a=" << a << endl;
return 0;
}

//结果如下
$ g++ -c main.cpp && g++ -c test.cpp && g++ main.o test.o -o main
test.o:(.data+0x0): multiple definition of `a'
main.o:(.bss+0x0): first defined here
collect2: error: ld returned 1 exit status

这不就是变量实验(2)嘛!

感想
如果把test.cpp改名为header.cpp就不难理解以下:
变量实验(4)就是平常所说的: extern用来告诉编译器,变量来自外部文件
变量实验(5)就是平常所说的: 头文件中声明变量,源文件中定义变量
额外函数实验(1)就是平常所说的: 头文件中声明函数,源文件中实现函数
额外函数实验(2)说明: extern对函数可以省略,头文件也可以做实现,源文件也可以做声明
额外变量实验说明: extern对变量不能省略
为什么extern对函数可以忽略而变量不可以: 因为对变量来说,”extern int a;”是声明, “int a;”是定义并默认初始化为0;而对函数来说,”extern void func();”是声明,”void func();”还是声明

纠正这些想法

  1. 头文件中 “int a;” 是声明变量 -> 不,它就是定义并初始化为0
  2. 头文件.h和源文件.cpp在声明和定义/实现上有不同行为 -> 不,一视同仁

以上。