目录

Linux下C开发环境

参考:C与C++编译过程-阮一峰的网络日志

检测与安装C开发环境

安装开发环境:

sudo apt-get install gcc gdb make autoconf automake libtool build-essential flex bison cmake git tree

检查C的开发环境: github check-c-dev.sh

工具介绍:

  • asGNU下的汇编工具

  • ldGNU下的链接工具

  • arGNU下的.o文件的归档工具,用于制作静态库.a

  • Lex是一种生成扫描器的工具。扫描器是一种识别文本中的词汇模式的程序。flexubuntu中的版本。

  • Yacc 代表 Yet Another Compiler CompilerYacc 的 GNU 版叫做 Bison。它是一种工具,将任何一种编程语言的所有语法翻译成针对此种语言的 Yacc 语法解析器。

gcc 概述

gcc -E xxx.c -o xxx.i               # 生成预编译文件, 内部 cpp 命令
gcc -S xxx.c -o xxx.s               # 生成汇编源文件, 内部 ccl 命令
gcc -c xxx.c -o xxx.o               # 生成目标文件, 内部 as 命令
ar rcsU zzz.a xxx.o yyy.o           # 打包成静态库
gcc -shared -fPIC xxx.c -o xxx.so   # 打包成动态库

gcc选项必须分立给出: -dr 完全不同于 -d -r,后面是详细参数:

通用

  • -o 指定输出的文件名

  • -std=gnu11 使用 gnu11 标准来编译

  • -pedantic 选项能够帮助程序员发现一些不符合 ANSI/ISO C标准 的代码

  • -Wall 开启所有警告错误提醒

  • -Werror 它要求gcc将所有的警告当成错误进行处理, 然后停止编译

  • -Wcast-align 当源程序中地址不需要对齐的指针指向一个地址需要对齐的变量地址时,则产生一个警告例如char *指向一个int *地址,而通常在机器中int是需要地址能被2或4整除的对齐地址

  • -masm = [ intel | att ] 选择intelAT&T(默认)的汇编语法

  • -pipe 在编译过程的不同阶段间使用管道而非临时文件进行通信

  • -O2 启用编译优化(可选g 1 2),代码效率更高更快

  • -v 输出编译详细过程

  • -Q 显示编译过程的统计数据和每一个函数名

  • -fpack-struct=4 强制对齐内存(可选 8 4 2)

  • -save-temps 保存编译过程中的中间文件(.i .s)

  • -fPIC 如果支持这种目标机,编译器就输出位置无关目标码.适用于动态库dynamic linking

调试相关

  • -g 开启gdb调试,默认级别2,此时产生的调试信息包括:扩展的符号表、行号、局部或外部变量信息

  • -g3包含级别2中的所有调试信息以及源代码中定义的宏

  • -p 产生额外代码,用于输出 profile 信息,供分析程序 prof 使用

  • -pg 产生额外代码,用于输出 profile 信息,供分析程序 gprof 使用

  • -DMACRO 以字符串 1 定义 MACRO宏; -DMACRO=DEFN,以字符串DEFN定义 MACRO 宏.与源代码中#define指令定义的宏效果一样

  • -UMACRO 取消对 MACRO 宏的定义

  • -rdynamic 用来通知链接器将所有符号添加到动态符号表中,目的是能够通过使用 dlopen 来实现向后跟踪

路径链接相关

  • -I./include 增加头文件搜索路径, 顺序: ./include、/usr/include、/usr/local/include

  • -lword : 链接libword.solibword.a库文件。连接器方法是:扫描归档文件,寻找某些成员,这些成员的符号目前已被引用,不过还没有被定义.但是,如果连接器找到普通的目标文件,而不是库文件,就把这个目标文件按平常方式连接进来

  • -L./lib 增加库文件搜索路径,顺序: ./lib/lib/usr/lib/usr/local/lib

  • -static 使用该选项,之后的-l全部强制使用.a

  • -pthread-lpthread :-pthread 会附加一个宏定义 -D_REENTRANT,让编译器选择那些thread-safe的实现;由于thread-safe的宏定义可能变化,因此在编译和链接时都使用-pthread选项而不是传统的-lpthread 能够保持向后兼容

  • 预处理器选项 :-Aassertion -C -dD -dM -dN -Dmacro[=defn] -E -H -idirafter dir -include file -imacros file -iprefix file -iwithprefix dir -M -MD -MM -MMD -nostdinc -P -Umacro -DMACRO

  • 汇编器选项: -Wa,option

  • 连接器选项: -llibrary -nostartfiles -nostdlib -static -shared -symbolic -Xlinker option -Wl,option -u symbol

  • 调试选项 -a -dletters -fpretend-float -gcoff -gxcoff -gxcoff+ -gdwarf -gdwarf+ -gstabs -gstabs+ -ggdb -print-file-name=library -print-libgcc-file-name -print-prog-name=program

GCC环境变量

gcc在编译时用到的环境变量

# 如果有多个目录,可以使用逗号隔开

# gcc和g++编译程序的搜索目录
export COMPILER_PATH=/usr/local/bin

# gcc 会读取该环境变量,然后在该目录中搜寻.h文件
export C_INCLUDE_PATH=/home/cky/hello/include

# 类似C_INCLUDE_PATH,适用于g++
export CPLUS_INCLUDE_PATH=/home/cky/hello/include

# gcc和g++在编译的链接(link)阶段查找库文件的目录列表,相当于在命令里使用-Ldir
export LIBRARY_PATH=/home/cky/hello/lib

使用ldd ./可执行程序名将看到可执行程序在执行时连接动态库的过程。

程序运行时用到的环境变量

# 程序运行时查找动态链接库(.so文件)的目录列表
export LD_LIBRARY_PATH=/home/cky/hello/lib

# 在LD_PRELOAD(参考man ld.so的LD_PRELOAD部分)中定义的动态链接库会在其他动态链接库之前被加载
# 因此会覆盖其他链接库里定义的同名符号(函数变量等)
export LD_PRELOAD=/usr/lib/libtsocks.so

Debian动态链接库搜索路径

Debian中如果修改LD_LIBRARY_PATH没有用,可修改/etc/ld.so.conf/etc/ld.so.conf.d/*.conf,将库目录作为一行加入以上的conf文件中,然后运行ldconfig命令即可。

$ vi /etc/ld.so.conf.d/my.conf # 添加下面两句

/usr/local/lib
/path/to/your/shared/lib/directory

$ ldconfig

库链接时搜索路径的顺序

静态库

  1. ld会先搜索GCC命令中-L指定的目录

  2. 再搜索gcc的环境变量LIBRARY_PATH

  3. 再搜索目录 /lib /usr/lib /usr/local/lib

动态库

  1. 编译目标代码时 -L 指定的目录

  2. 环境变量 LD_LIBRARY_PATH 指定的动态库搜索路径

  3. 配置文件 /etc/ld.so.conf 中指定的动态库搜索路径

  4. 默认的动态库搜索路径/lib

  5. 默认的动态库搜索路径/usr/lib

同时使用动态库和静态库

场景:写一个Nginx模块,使用MySQL的C客户端接口库libmysqlclient,当然mysqlclient还引用了其他的库,比如libm, libz, libcrypto等等。对于使用mysqlclient的代码来说,需要关心的只是mysqlclient引用到的动态库。大部分情况下,不是每台机器都安装有libmysqlclient,所以我想把这个库静态链接到Nginx模块中,但又不想把mysqlclient引用的其他库也静态的链接进来。

简单地使用-static显得有些暴力,因为他会把命令行中-static后面的所有-l指明的库都静态链接,更主要的是,有些库可能并没有提供静态库.a,而只提供了动态库.so。这样的话,使用-static就会造成链接错误。

-Wl,option 将选项 option 传递给链接器。-Wl,-Bstatic告诉链接器,对接下来的-l选项使用静态链接。-Wl,-Bdynamic 告诉链接器对接下来的-l选项使用动态链接

# 修改前
CORE_LIBS="$CORE_LIBS -L/usr/lib64/mysql -lmysqlclient -lz -lcrypt -lnsl -lm -L/usr/lib64 -lssl"

# 修改后
CORE_LIBS="$CORE_LIBS -L/usr/lib64/mysql -Wl,-Bstatic -lmysqlclient \
-Wl,-Bdynamic -lz -lcrypt -lnsl -lm -L/usr/lib64 -lssl"

头文件的搜索路径顺序

  1. -Idir开始

  2. 然后找gcc的环境变量C_INCLUDE_PATH CPLUS_INCLUDE_PATH OBJC_INCLUDE_PATH

  3. 再找默认目录

/usr/include
/usr/local/include
/usr/lib/gcc-lib/i386-linux/2.95.2/include
/usr/lib/gcc-lib/i386-linux/2.95.2/../../../../include/g++-3
/usr/lib/gcc-lib/i386-linux/2.95.2/../../../../i386-linux/include
  1. 如果装有gcc,并且有给定的prefix的话,则是找:

/usr/include
prefix/include
prefix/xxx-xxx-xxx-gnulibc/include
prefix/lib/gcc-lib/xxxx-xxx-xxx-gnulibc/2.8.1/include

利用Linux系统上已有的开发库

  • 查看库文件提供了哪些调用,可以用nm命令自己查看库文件提供了哪些调用

  • 通过头文件查看函数的定义

动态链接库