对于.c 格式的C文件,可以采用 gcc 或 g++编译。
对于 .cc、.cpp 格式的C++文件,应该采用 g++进行编译。
常用的选项:
选项 | 作用 |
-c <源文件> | 表示编译源文件(.c,.cpp,.cc等) |
-o <输出文件> | 表示输出目标文件(.o) |
-g | 表示在目标文件中产生调试信息,用于 gdb 调试 |
-D <宏定义> | 编译时将宏定义传入进去 |
-Wall | 打开所有类型的警告。 |
注意:这个编译器编译出来的文件是为了在X86的linux系统环境下运行的。
过程:预编译à编译à汇编à链接

当我们进行编译的时候,要使用一系列的工具,我们称之为工具链。其中包括:预处理器、编译器、汇编器as、连接器。
一个编译过程可以用图2.1 来表示, 包括下面几个阶段:
(1)预处理:预处理器将对源文件中的宏进行展开。
(2)编译:gcc 将 c 文件编译成汇编文件(.s)。
(3)汇编:as 将汇编文件编译成机器码(.o)。
(4)链接:将目标文件和外部符号进行连接,得到一个可执行二进制文件。
后缀名 | 所对应的语言 |
| 后缀名 | 所对应的语言 |
.c | C原始程序 | .s/.S | 汇编语言原始程序 | |
.C/.cc/.cxx | C++原始程序 | .h | 预处理文件(头文件) | |
.m | Objective.c原始程序 | .o | 目标文件 | |
.i | 已经预处理的C原始程序 | .a/.so | 编译后的库文件 | |
.ii | 已经预处理的C++原始程序 |
|
|
选项 | 含义 |
-E | 只预编译,不做任何处理 |
-c | 只编译不链接,生成目标文件”.o” |
-S | 只编译不会汇编,生成汇编代码”.s” |
-g | 在执行过程中包含标准调试信息 |
-o file | 指定将file文件作为输出文件 |
-v | 打印出编译器内部编译各过程的命令行信息和编译器的版本 |
-I dir | 在头文件的搜索路径列表中添加dir目录 |
例子:
下面以一个很简单的test.c 来探讨这个过程。
#include
#define NUMBER (1 + 2)
int main()
{int x = NUMBER;return 0;
} 预处理: gcc -E test.c -o test.i。
我们用cat 查看 test.i 的内容如下:
...... //大多都是stdio.h文件内容# 943 "/usr/include/stdio.h" 3 4# 3 "test.c" 2int main(void){int x = (1+2);return 0;}我们可以看到,文件中宏定义NUMBER 出现的位置被(1+2)替换掉了,还有把stdio.h的内容都添加进来了。
编译: gcc -S test.i -o test.s。
通过cat test.s 查看test.s 的内容为汇编代码如下:
junjia@junjia:~/works/3rd_day/1st_test$ cat test.s.file "test.c".text.globl main.type main, @functionmain:.LFB0:.cfi_startprocpushl %ebp.cfi_def_cfa_offset 8.cfi_offset 5, -8movl %esp, %ebp.cfi_def_cfa_register 5subl $16, %espmovl $3, -4(%ebp)movl $0, %eaxleave.cfi_restore 5.cfi_def_cfa 4, 4ret.cfi_endproc.LFE0:.size main, .-main.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4".section .note.GNU-stack,"",@progbitsjunjia@junjia:~/works/3rd_day/1st_test$ 汇编: as test.s -o test.o 。
利用as 将汇编文件编译成机器码。得到输出文件为 test.o,test.o 中为目标机器上
的二进制文件。
用 nm 查看文件中的符号: nm test.o ,输出如下:
00000000 T main有的编译器上会显示:
00000000 b .bss 00000000 d .data 00000000 t .text U ___main U __alloca 00000000 T _main 既然已经是二进制目标文件了,能不能执行呢?
试一下./test.o,提示 cannot execute binary file。
因为是有U这样的符号的地址没有定下来。
链接:gcc -o test test.o。
将所有的.o 文件链接起来生成可执行程序,这个时候的test就可以执行了。
一般我们编译文件没有必要那么多步骤,只要如下就够了:
gcc -o teat test.c步骤总结:
预处理阶段:对包含的头文件(#include)和宏定义( #define、 #ifdef 等)进行处理。
gcc –E hello.c –o hello.i
//-o 表示输出为指定文件类型 -E 将源文件( *.c) 转换为( *.i)编译阶段:检查代码规范性、语法错误等,在检查无误后把代码翻译成汇编语言。
gcc –S hello.i –o hello.s
//-S 将已预处理的 C 原始程序( *.i)转换为( *.s) 链接阶段:将.s 的文件以及库文件整合起来链接为可执行程序。
gcc –o hello.exe hello.s
//最后将汇编语言原始程序(*.s)和一些库函数整合成( *.exe) 示例1:几种编译过程。
#include
#define MAX 100
#define max(a, b) ((a) > (b) ? (a) : (b)) //宏定义,执行-E 之后被替换
main()
{printf("MAX=%d\n", MAX);printf("max(3,4)=%d\n", max(3, 4));
} //法一:
gcc –E project1.c –o project1.i //预编译,生成已预编译过的 C 原始程序*.i
gcc –S project1.i –o project1.s //编译,生成汇编语言原始程序*.s
gcc –o project1.exe project1.s //链接,生成可执行程序
//法二:
gcc –c project1.c –o project1.o //编译
gcc –o project1.exe project1.o //链接
//法三:
gcc –o project1.exe project1.c //编译并链接
示例2:-D选项的使用。
#include
main()
{#ifdef lry //表示如果定义了 lry,即命令行参数传了 lry,就执行下面的输出printf("lry is defined!\n");#elseprintf("lry is not defined!\n");#endifprintf("main exit\n");
} 编译过程:
gcc –E project2.c –o project2.i –D lry
//条件编译,用-D 传递,如果没有传 lry 则执行#else
gcc –S project2.i –o project2.s
gcc –o project2.exe project2.s
或:gcc –o project2 project2.c –D lry
作用:对c/c++程序进行调试的。
1.让程序停止在指定的某处
2.当程序停住时,可以查看程序的运行状态(比如:变量值)
第1步:编译程序
gcc -g a.c -o a.out
第2步:启动GDB
gdb a.out //运行调试器并打开调试文件
或者:
gdb //运行gdb
file a.out //打开调试文件
第3步:打断点
break 函数名 //在函数入口处打断点
或者
break 行号 //在某行打断点
第4步:运行程序
run
其他命令:括号内是命令的简写。
序号 | 命令 | 作用 |
1 | next,continue |
|
2 | list(l) |
|
3 | break(b) | 函数名/行号/文件名:行号 |
4 | break行号 if 条件 | break 5 if k=8 |
5 | info break | 查看断点信息 |
6 | delete <断点编号> | 删除断点 |
7 | run(r) | 全速运行到断点处 |
8 | next(n) | 单独运行,但不进入子函数 |
9 | step(s) | 单独运行,但进入子函数 |
10 | continue(c) |
|
11 | print(p) |
|
12 | finish |
|
13 | watch 变量名 | 查看变量的值 |
下一小节:Makefile的使用
上一篇:linux权限与远程管理