《关于GCC编译》

概述

        GCC是GNU Compiler Collection的缩写,他是一个编译工具集。很多人都知道怎么用gcc命令编译生成一个可执行文件,其基本用法也可以通过help文件或者man手册查看。本文期望给大家整理一些gcc的常用且重要的用法。

1. GCC编译的四个阶段及其控制选项

        gcc实际上集合了源码编译的四个过程:预编译(cpp)、编译(cc)、汇编(as)、链接(ld)。gcc专门提供了四个选项来控制编译的程度:

        -o file   :四个过程全部执行,最终生成一个可执行文件"file",没有-o选项时默认生成“a.out”。

        -E         :只执行到预处理,例如宏定义的扩展,结果输出到标准输出。

        -S         :只执行到编译,将输入源文件转换成汇编语言,默认生成"源文件名.s"。

        -c          :只执行到汇编,将输入文件转换成目标文件"源文件名.o",但不链接,常用于制作静态库。

2. 预编译检查宏定义

        预编译处理以#开头的预编译指令,包括#define宏定义,条件编译和#include头文件包含。预编译实际进行的是文本的替换工作,因此根据预编译的结果可以检查宏定义是否正确。

#include <stdio.h>
#include <stdlib.h>

#define max(x,y) ((x)>=(y))?(x):(y)

int main (int argc, char** argv)
{
    int a = atoi(argv[1]);
    int b = atoi(argv[2]);

    printf("max number is: %d\n",max(a,b));

    return 0;
}
        上面的代码,预编译后我们可以在输出内容最后看到下面的内容:gcc -E test.c   |  less

int main (int argc, char** argv)
{
    int a = atoi(argv[1]);
    int b = atoi(argv[2]);

    printf("max number is: %d\n",((a)>=(b))?(a):(b));

    return 0;
}
        通过检查预编译后的文件我们可以清楚看到宏定义是否正确,有兴趣的话可以继续往上看包含的头文件,你会发现头文件是一层一层嵌套包含的,经过预编译之后全部一层一层地替换到最终文件里了。

3. 静态库和动态库的生成与使用

        对于静态库和动态库在编译到最终目标文件时有点区别,静态库会被编译进目标文件,而动态库只编译链接关系到目标文件,目标文件执行时才回去调用动态库文件。两种方式国有优缺点,这里只谈怎么生成静态库文件和动态库文件。

foo.c :
#include <stdio.h>

void foo(void)
{
    printf("I am foo\n");
}

main.c :
#include <stdio.h>

void foo();

int main(int argc, char** argv)
{
    printf("I am main,call foo:\n");
    foo();

    return 0;
}
        首先用gcc编译生成.o文件,然后再根据需要用ar命令创建静态库文件,或者使用gcc创建动态库文件:
        gcc -c foo.c

        静态库:

        1)生成.o文件:gcc -c foo.c

        2)生成静态库:ar csr libfoo.a foo.o

        3)使用静态库:gcc -o test1 main.c -L. -lfoo

        说明:-L和-l选项下面会谈到,分别制定库搜索目录和库名(库名不包含前缀lib和后缀.a)。还有一点,gcc从右向左检查文件的依赖关系,main函数调用foo函数,所以main函数写在静态库foo的左边,否则会报错。这一点非常重要,在多个库嵌套包含的时候,也要特别注意编译时的顺序问题。

        动态库:

        1)生成.o文件:gcc -fPIC -c foo.c

        2)生成动态库:gcc -shared -o libfoo.so foo.o

        3)使用动态库:gcc -o test2 main.c -L. -lfoo

        说明:依赖关系和静态库一样。此外,生成动态库的时候,必须在准备.o文件时添加参数-fPIC。否则会报一下错误:

        /usr/bin/ld: foo.o: relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC
        foo.o: could not read symbols: Bad value
        collect2: ld returned 1 exit status


4. GCC的常用编译选项

        gcc选项很多,可以通过help或者man手册详细查阅。这里仅列出一些常用的选项。

        1)调试选项

        常用的调试选项是 -g。加了-g选项生成的可执行文件才能使用gdb调试。
        -g : 产生调试器GDB所需要的符号信息。

        2)目录选项。

        常用的目录选项也只有两个。
        -Idir : 添加头文件搜索的目录列表。一般GCC搜索头文件的默认目录是/usr/include。如果在工程应用中程序员自己定义了一个头文件目录,就用可以使用-I将该目录追加上。
        -Ldir:添加库文件搜索的目录列表。一般GCC搜索库文件的默认目录是/usr/lib。如果在工程项目中程序员自己生成了很多的库文件,就可以使用-L 链接,将该库文件的搜索目录追加上。

        3)链接选项。

        -llibrary:连接名为 library 的库文件,库文件的真正名字是`liblibrary.a'
        -static:静态编译。该选项是不使用动态库,将所有的链接库都整合到可执行文件中去。可以看出加该参数的编译方式的可执行文件的大小比没有加该参数的编译方式大很多。使用静态编译常用于反汇编一些可执行文件,这样把库中都做了些什么都可以看得到。

        4)宏的预定义。

        -D name[=value]:在编译时通过-D来定义宏,通常用于条件编译,以及动态传递变量。实际工程中常遇到同一分代码需要适应多种场合,这时常使用到-D选项。


你可能感兴趣的:(预编译,动态库,静态库,gcc编译,-static)