gcc-basic

GCC 基础教程

1 简介

  • GCC: GNU C Compiler => GNU Compiler Collection

2 编译

编译单个文件:

1
gcc -Wall hello.c -o hello

  • -o: 指明输出文件
  • -Wall: 开启所有编译警告信息

编译多个文件:

1
gcc -Wall main.c hello_fn.c -o newhello

只编译修改的文件:

1
2
3
gcc -Wall -c main.c
gcc -Wall -c hello_fn.c
gcc main.o hello_fn.o -o hello
  • -c: 编译为 object 文件, 生成 main.o

注意 object 文件链接的顺序 ( main.o 调用 hello_fn.o ):

1
gcc main.o hello_fn.o -o hello

而不是 (错误):

1
gcc hello_fn.o main.o -o hello

链接外部库:

系统库一般存放在:

  • /usr/lib or /usr/lib64
  • /lib or /lib64
1
gcc -Wall calc.c /usr/lib/libm.a -o calc
1
gcc -Wall calc.c -lm -o calc
  • -l: 链接库
  • -lNAME: 在标准路径下寻找 libNAME.a

这样的链接顺序是不行的:

1
gcc -Wall -lm calc.c -o calc

3 常用编译参数

GCC 默认搜索头文件路径:

  • /usr/local/include/
  • /usr/include/

默认搜索库文件:

  • /usr/local/lib/
  • /usr/lib/

参数:

  • -I: 从头部增加头文件搜索路径
  • -L: 从头部增加库搜索路径

举例:

1
gcc -Wall -I/opt/gdbm-1.8.3/include -L/opt/gdbm-1.8.3/lib dbmain.c -lgdbm

  • C_INCLUDE_PATH: C 头文件
  • CPLUS_INCLUDE_PATH: C++ 头文件
  • LIBRARY_PATH: 库文件
1
2
C_INCLUDE_PATH=.:/opt/gdbm-1.8.3/include:/net/include
LIBRARY_PATH=.:/opt/gdbm-1.8.3/lib:/net/lib
  • .: 当前路径
  • -I-L: 都可以重复
1
2
gcc -I. -I/opt/gdbm-1.8.3/include -I/net/include
-L. -L/opt/gdbm-1.8.3/lib -L/net/lib .....

动态库默认搜索路径:

  • /usr/local/lib
  • /usr/lib

或者添加环境变量:

  • LD_LIBRARY_PATH
1
2
gcc -Wall -static -I/opt/gdbm-1.8.3/include/
-L/opt/gdbm-1.8.3/lib/ dbmain.c -lgdbm
  • -static: 强制静态链接, 避免使用动态库:
1
2
gcc -Wall -static -I/opt/gdbm-1.8.3/include/
-L/opt/gdbm-1.8.3/lib/ dbmain.c -lgdbm

使用静态链接库:

1
2
gcc -Wall -static -I/opt/gdbm-1.8.3/include/
-L/opt/gdbm-1.8.3/lib/ dbmain.c -lgdbm

使用动态链接库:

1
2
gcc -Wall -I/opt/gdbm-1.8.3/include
dbmain.c /opt/gdbm-1.8.3/lib/libgdbm.so

  • -ansi: 禁止 GNU 对于 C语言 标准扩展
  • -pedantic: 禁止所有 GNU C 扩展
  • -std=c89
  • -std=c99

4 使用预处理器

  • cpp: GNU C preprocessor
1
2
3
4
5
6
7
8
9
int
main (void)
{
#ifdef TEST
printf ("Test mode\n");
#endif
printf ("Running...\n");
return 0;
}
1
gcc -Wall -DTEST dtest.c
  • -DNAME: 定义一个预处理宏 NAME

列举 GNU 默认定义的宏:

1
cpp -dM /dev/null

带有值的宏:

1
2
gcc -Wall -DNUM=100 dtestval.c
gcc -Wall -DNUM="2+2" dtestval.c

观察源代码被预处理器处理后的文件:

1
gcc -E test.c
  • -E: 运行预处理器

5 调试

1
gcc -Wall -g null.c
  • -g: 用来记录程序 crash 的时候当时的环境情况

出现如下错误的时候,会生成一个 core 文件:

1
Segmentation fault (core dumped)

ulimit -c 控制 core 文件的最大数量

1
ulimit -c unlimited

注意,同一个 terminal 只能被设置一次,否则就会弹出错误:

1
bash: ulimit: core file size: cannot modify limit: Operation not permitted

可以重新打开一个新的 terminal 来设置:

core 文件可以被 GNU Debugger gdb 加载:

1
gdb a.out core

然后可以接着在 gdb 这个交互式命令中输入:

1
2
3
(gdb) print p
(gdb) backtrace
(gdb) ...

6 优化

7 编译 C++ 程序

1
g++ -Wall hello.cc -o hello

8 平台相关选项

  • -march=CPU: 指定 CPU

9 Troubleshooting

  • –help: 显示帮助
  • –version: 版本号
  • -v: 命令执行的详细信息

10 编译器相关的工具

  • ar: 将一系列的 object 文件合并为单个 archive 文件,即库
1
2
ar cr libhello.a hello_fn.o bye_fn.o
ar t libhello.a
  • c: create
  • r: replace
  • t: table of contents

  • gprof: 测量性能
1
2
3
gcc -Wall -c -pg collatz.c
gcc -Wall -pg collatz.o
gprof a.out
  • -gp: profiling option

  • gcov: 测量执行了多少行
1
2
gcc -Wall -fprofile-arcs -ftest-coverage cov.c
gcov cov.c

11 编译器是如何工作的

  • 预处理: cpp hello.c > hello.i
  • 编译: gcc -Wall -S hello.i
  • 汇编: as hello.s -o hello.o
  • 链接: ld -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/gcc-lib/i686/3.3.1/crtbegin.o -L/usr/lib/gcc-lib/i686/3.3.1 hello.o -lgcc -lgcc_eh -lc -lgcc -lgcc_eh /usr/lib/gcc-lib/i686/3.3.1/crtend.o /usr/lib/crtn.o
  • 运行: ./a.out

12 检查可执行文件和 object 文件的内容

  • file: 查看 object 文件或者可执行文件的内容
1
file a.out

输出:

1
a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), not stripped
  • ELF: The internal format of the executable file (ELF stands for “Executable and Linking Format”, other formats such as COFF “Common Object File Format” are used on some older operating systems (e.g. MS-DOS)).
  • 32-bit: The word size, 一些平台可能是 64 字节
  • LSB: least significant byte first word ordering
  • Inter 80386: 处理器
  • version 1 (SYSV): 文件格式的版本
  • dynamically linked: 使用了动态链接库
  • not stripped: The executable contains a symbol table (this can be removed with the strip command).

  • nm: 显示符号表, 存储了根据名字标识的函数和变量
1
nm a.out

  • ldd: 检查可执行文件列出它所需要的动态链接库
1
ldd a.out

Basics of Working with gdb

gdb has a command-line interface similar to a Unix shell. You type a command and then press the Enter key (回车) to execute it. If you have never worked with gdb before, begin by executing the help command (帮助命令).

help 还可以 help 子命令,例如:

1
help running

参考

推荐文章