C++编译&&运行
本文以
UNIX
环境为主,结合一些技术博客和<<C++ Primer>>
做一些总结和整理。
编译过程
单文件
这里的单文件指的是单独的 .cpp/.c
文件,因为 .h
文件只是进行一些变量的声明,是不需要进行编译的。
预处理(预处理器
cpp
): 预处理器cpp将对源文件中的宏进行展开1
2
3gcc -E hello.cpp -o hello.i
// or
cpp hello.cpp -o hello.i编译(编译器
gcc/g++
): gcc将c文件编译成汇编文件,然后编译成机器码。(编译器将.i
文件编译成汇编文件.s
)。1
gcc -S hello.i
汇编(汇编器
as
): 汇编器将汇编文件编译成机器码 (可以通过nm -a hello.o
查看机器码文件)1
2
3gcc -c hello.s -o hello.o
// or
as hello.s -o hello.o链接(连接器
ld
): 链接器ld将目标文件和外部符号进行连接,得到一个可执行二进制文件。1
gcc hello.o -o hello
头文件
我们在第一步当中可以看到,这一步的作用就是把宏进行了展开。那么我们的头文件也是在这里被以宏的方式引入到了 hello.cpp
当中。那么我们下面展开介绍一下 #include
与头文件中的一些注意事项。
#include
#include
是c语言的宏命令,会在第一步(预处理)中起作用。会将后面那个头文件完完整整的引入到当前的文件当中。而且仅是做替换,而不会有其他的副作用。
1 | // math.h |
1 | // main.cpp |
经过第一步预处理以后:
1 |
|
而对于头文件的声明当中 " "
和 < >
是有区别的,如果头文件名在 < >
中,就会被认为是标准头文件。编译器会在预定义的位置查找该头文件,如果是 " "
就认为它是非系统头文件,非系统文件查找通常开始于源文件所在路径。
头文件保护符
头文件保护符是为了保证头文件在一个 .cpp
文件当中被多次引用不会出现问题。
1 | // file1.h |
上面file3.h
的代码展开以后就变成了
1 | // file1.h展开的内容 |
class file1
被引用了两次,导致编译器报错。这时就可以加上头文件保护符来解决这个问题。
1 | // file1.h |
1 | // file2.h |
1 | // file3.h |
这时因为 _FILE1_H_
只出现了一次,就不会出现重定义的问题。
注意事项
头文件中需要区别 声明 与 定义 两个概念。声明因为不涉及到内存的分配,所以是允许多次出现的,而定义则会进行内存的分配,所以定义只能出现一次。而在头文件中是只允许出现声明和一些特殊的定义的( 类定义, 值在编译时已知的 const
对象和 inline
函数)
值在编译时就已知的
const
对象:
如:const char c = 'c'
这个是在编译时就已经确定值的,之后程序不能改变。
而const char *c = 'c'
是不可以的,因为指针不是在编译时确定值的。
并且全局的const
对象是没有extend
的声明的,所以只对当前.cpp
文件有效。所以将它放在头文件中进行引用后,仅对引用文件有效,而对其他文件不可见。所以不会出现重定义。inline
:因为在函数的调用执行过程当中,我们需要将实参、局部变量、返回地址以及若干寄存器都压入栈中,然后才能执行函数体中的代码;函数体中的代码执行完毕后还要清理现场,将之前压入栈中的数据都出栈,才能接着执行函数调用位置以后的代码。如果一个运行时间很长的函数,那么这些调用代价也是可以忽略的。不过对于一些简单的函数,这些调用代价是很昂贵的。我们就可以使用inline
函数,它会像宏定义一样进行代码的替换,而不进行调用过程。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using namespace std;
inline void swap(int *a, int *b){
int temp;
temp = *a;
*a = *b;
*b = temp;
}
int main(){
int m, n;
cin>>m>>n;
cout<<m<<", "<<n<<endl;
swap(&m, &n);
cout<<m<<", "<<n<<endl;
return 0;
}在编译器遇到函数调用
swap(&m, &n)
的时候就会进行替换,并用实参代替形参。1
2
3
4int temp;
temp = *(&m);
*(&m) = *(&n);
*(&n) = temp;
class
:对于类的定义成员函数是可以写在定义体内的,这样的话编译器会默认这个函数是inline
的,也可以声明在定义体内然后在外面进行实现。
多文件
多文件互相依赖的情况,只需要先单独将各文件编译成 .o
文件,然后 link
一下就行了。
1 | // Circle.h |
1 | // Circle.cpp |
1 | // main.cpp |
进行编译:
1 | $ g++ -c Circle.cpp -o Circle.o |
如果有修改,每次只需要对增量文件进行编译就行了。如果项目比较大,可以使用 makefile
文件来进行自动化编译。(后面会有文章继续介绍 makefile
和其他的自动化编译)