没有输出的输入是不完整的

0%

初探c++编译过程

在科研项目中碰到了一个c++的程序,需要自己进行编译,所以就顺带着复习了一波c++的编译过程。
本篇文章探究c++编译过程的第一篇,主要尝试编译单个源文件和多个源文件生成可执行文件。

单个源文件生成可执行程序

  1. 编写helloworld源代码,文件后缀是cpp
1
2
3
4
5
6
/* helloworld.cpp */
#include <iostream>
int main(){
std::cout<<"hello world"<<std::endl;
return 0;
}
  1. 通过gcc编译器对其进行编译
1
2
3
g++ helloworld.cpp
或者
g++ helloworld.cpp -o helloworld

如果是第一条命令,g++会编译helloworld.cpp文件产生a.out文件,a.out是默认文件名;
但是更多情况下我们使用g++编译文件的时候会通过-o参数来指定生成的可执行文件的文件名。比如这里生成的文件名就叫做helloworld

  1. 查看当前目录文件
1
ls

因为执行了两次不同的编译,所以当前目录下会有三个文件,如下所示。

1
a.out  helloworld  helloworld.cpp
  1. 执行文件
1
2
3
./a.out
或者
./helloworld

这里两次结果是一样的

1
hello world

两个源文件生成可执行程序

这一部分我们自己编写一个头文件speak.h,然后编写一个测试文件speaktest.cpp进行编译测试。

  1. 编写speak.h代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
speak.h
用于测试g++编译多个文件形成一个可执行文件
*/
#ifndef __SPEAK__
#define __SPEAK__
#include <iostream>
class Speak{
public:
void sayHello(const char* );
};

void Speak::sayHello(const char* str){
std::cout<< "hello" <<'\t'<< str << std::endl;
}

#endif
  1. 编写speaktest.cpp代码
1
2
3
4
5
6
7
8
9
10
11
/*
speaktest.cpp
用于测试g++编译多个文件形成一个可执行文件
*/

#include "speak.h"
int main(){
Speak speak;
speak.sayHello("kingwen");
return 0;
}
  1. g++编译
1
g++ speaktest.cpp -o speaktest
  1. 执行结果
1
./speaktest

结果很符合我们的预期

1
hello   kingwen

所以我们可以得出很明显的结论,就是我们的.h其实只要在最终的测试文件中include,不需要自己再手动编译一次,这非常符合编译原理课程中的讲述。

这里我们就发现一个问题,我们的speak.h头文件其实和它的实现都在同一个文件中,但是很多情况下,头文件只负责声明,具体实现是通过其他文件来实现的,如果碰到这种情况我们还可以通过只编译测试文件就能运行嘛?来测试一下。

声明和实现分离生成可执行程序

  1. 编写talk.h文件
1
2
3
4
5
6
7
8
9
/*
talk.h
用于测试当talk的声明和实现分离,那么在测试的时候能不能只编译测试文件。
*/

class Talk{
public:
void say(const char*);
};
  1. 编写talk.cpp文件
1
2
3
4
5
6
7
8
9
10
/*
talk.cpp
用于测试当talk的声明和实现分离,那么在测试的时候能不能只编译测试文件。
*/

#include "talk.h"
#include <iostream>
void Talk::say(const char* str){
std::cout<<"talk with"<<'\t'<<str<<std::endl;
}
  1. 编写talktest.cpp文件
1
2
3
4
5
6
7
8
9
10
/*
talktest.cpp
用于测试当talk的声明和实现分离,那么在测试的时候能不能只编译测试文件。
*/
#include "talk.h"
int main(){
Talk talk;
talk.say("kingwen");
return 0;
}
  1. 尝试编译
1
g++ talktest.cpp -o talktest

注意,这个时候报错了!!!

1
2
3
4
5
6
➜  compilec++ g++ talktest.cpp -o talktest
Undefined symbols for architecture x86_64:
"Talk::say(char const*)", referenced from:
_main in talktest-2630f3.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

原因其实是没有找到Talk中say方法的实现。
那么我们尝试连同talk.cpp一起编译

1
g++ talktest.cpp talk.cpp -o talktest

这个时候就可以编译通过啦,而且经过我的测试中间talktest.cpp和talk.cpp的顺序交换是没有关系的。
5. 执行结果

1
./talktest

结果非常符合预期

1
talk with   kingwen

由此,我们可以解释我们小节提出的疑问了,如果声明和实现不在同一个地方,那么对于实现文件是需要进行编译的,否则就会报错。

参考

  1. GCC编译C++