在科研项目中碰到了一个c++的程序,需要自己进行编译,所以就顺带着复习了一波c++的编译过程。
本篇文章探究c++编译过程的第一篇,主要尝试编译单个源文件和多个源文件生成可执行文件。
单个源文件生成可执行程序
编写helloworld源代码,文件后缀是cpp
1 2 3 4 5 6 /* helloworld.cpp */ #include <iostream> int main(){ std::cout<<"hello world"<<std::endl; return 0; }
通过gcc编译器对其进行编译
1 2 3 g++ helloworld.cpp 或者 g++ helloworld.cpp -o helloworld
如果是第一条命令,g++会编译helloworld.cpp文件产生a.out文件,a.out是默认文件名;
但是更多情况下我们使用g++编译文件的时候会通过-o参数来指定生成的可执行文件的文件名。比如这里生成的文件名就叫做helloworld
查看当前目录文件
因为执行了两次不同的编译,所以当前目录下会有三个文件,如下所示。
1 a.out helloworld helloworld.cpp
执行文件
这里两次结果是一样的
两个源文件生成可执行程序
这一部分我们自己编写一个头文件speak.h,然后编写一个测试文件speaktest.cpp进行编译测试。
编写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
编写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; }
g++编译
1 g++ speaktest.cpp -o speaktest
执行结果
结果很符合我们的预期
所以我们可以得出很明显的结论,就是我们的.h其实只要在最终的测试文件中include,不需要自己再手动编译一次,这非常符合编译原理课程中的讲述。
这里我们就发现一个问题,我们的speak.h头文件其实和它的实现都在同一个文件中,但是很多情况下,头文件只负责声明,具体实现是通过其他文件来实现的,如果碰到这种情况我们还可以通过只编译测试文件就能运行嘛?来测试一下。
声明和实现分离生成可执行程序
编写talk.h文件
1 2 3 4 5 6 7 8 9 /* talk.h 用于测试当talk的声明和实现分离,那么在测试的时候能不能只编译测试文件。 */ class Talk{ public: void say(const char*); };
编写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; }
编写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 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. 执行结果
结果非常符合预期
由此,我们可以解释我们小节提出的疑问了,如果声明和实现不在同一个地方,那么对于实现文件是需要进行编译的,否则就会报错。
参考
GCC编译C++