预处理、编译、汇编、链接

预处理阶段:写好的高级语言的程序文本比如hello.c,预处理器根据#开头的命令,修改原始的程序,如#include#include,将把系统中的头文件插入到程序文本中,通常是以.i.i结尾的文件。
编译阶段:编译器将hello.i文件翻译成文本文件hello.s,这个是汇编语言程序。高级语言是源程序。所以注意概念之间的区别。汇编语言程序干嘛?每条语句都以标准的文本格式确切描述一条低级机器语言指令。不同的高级语言翻译的汇编语言相同。
汇编阶段:汇编器将hello.s翻译成机器语言指令。把这些指令打包成可重定位目标程序,即.o.o文件。hello.o是一个二进制文件,它的字节码是机器语言指令,不再是字符。前面两个阶段都还有字符。
链接阶段:比如hello程序调用printf程序,它是每个C编译器都会提供的标准库C的函数。这个函数存在于一个名叫printf.o的单独编译好的目标文件中,这个文件将以某种方式合并到hello.o中。链接器就负责这种合并。得到的是可执行目标文件。

文件组织

参考:http://c.biancheng.net/view/7672.html
#include 是一个来自 C 语言的宏命令,作用于程序执行的预处理阶段,其功能是将它后面所写文件中的内容,完完整整、一字不差地拷贝到当前文件中。

防止重复包含

1) 使用宏定义避免重复引入

头文件会被放入include该头文件的cpp文件中。头文件的内容一般都会使用条件编译预处理语句(如上)包住,防止因为依赖关系多次被包含。
#ifndef 的特点是可移植性高,编译效率差。

  1. //header.h
  2. #ifndef HEADER_H
  3. #define HEADER_H
  4. void fun();
  5. #endif

2) 使用#pragma once避免重复引入

  1. #pragma once

只能作用于整个头文件,不能作用于一段代码。有些编译器不支持。

3) 使用_Pragma操作符

  1. _Pragma("once")

头文件内容

类声明、结构声明
一般函数声明、static inline函数的声明及实现、模板函数(?)
例外:1) 头文件中可以定义 const 对象 2) 头文件中可以定义内联函数 3) 头文件中可以定义类
static变量和全局的 const 对象。全局的 const 对象默认是没有 extern 声明的,所以它只在当前文件中有效。把这样的对象写进头文件中,即使它被包含到其他多个 .cpp 文件中,这个对象也都只在包含它的那个文件中有效,对其他文件来说是不可见的,所以便不会导致多重定义。与此同时,由于这些 .cpp 文件中的 const 对象都是从一个头文件中包含进去的,也就保证了这些 .cpp 文件中的 const 对象的值是相同的,可谓一举两得。
在 C++ 的类中,如果成员函数直接定义在类体的内部,则编译器会将其视为内联函数。所以把函数成员的定义写进类体内,一起放进头文件中,也是合法的。注意,如果把成员函数的定义写在定义类的头文件中,而没有写进类内部,这是不合法的。这种情况下,此成员函数不是内联函数,一旦头文件被两个或两个以上的 .cpp 文件包含,就可能会出现重定义的错误。

cpp文件内容

对应的函数实现
static函数的声明和实现

g++多文件构建

预处理,把.h文件插入cpp文件中,替换宏定义,生成.i文件

  1. g++ -E main.cpp -o main.i
  2. g++ -E student.cpp -o student.i

编译,生成汇编代码.s文件

  1. g++ -S main.i -o main.s
  2. g++ -S student.i -o student.s

汇编,生成机器码.o文件

  1. g++ -c main.s -o main.o
  2. g++ -c student.s -o student.o

链接

  1. g++ main.o student.o -o student.exe

直接一条命令构建

  1. g++ main.cpp student.cpp -o student.exe

常用构建方式,分成预处理编译汇编+链接两步

  1. g++ -c main.cpp -o main.o
  2. g++ -c fun.cpp -o fun.o
  3. g++ main.o fun.o -o out

把链接步骤分出来,好处是可以灵活的修改部分cpp文件而不用重新编译汇编全部文件

g++常用选项

  1. -std=c++11
  2. g++ -pthread linux上使用了多线程库时需要加上-pthread

asan内存越界检查