GCC编译C/C++

来源:互联网 发布:手机淘宝如何一件代发 编辑:程序博客网 时间:2024/05/16 11:12

编译C程序

多源文件到可执行文件

gcc编译程序可以自动处理连接,甚至在编译多个源文件的时候也可以自动连接
sayhello.c

#include <stdio.h>void sayhello(void) {    printf("hello, world\n");}

hellomain.c

void sayhello(void);int main(int argc, char *argv[]) {    sayhello();    return 0;}
$ gcc hellomain.c sayhello.c -o hello另外$ objdump -f hello  // objdump工具用于分析.o/.a/.so/ELF文件的信息,-f选项显示完整文件头内容

只进行预处理

-E选项指出,源代码只传递给预处理程序,结果默认显示在标准输出stdout上。

$ gcc -E sayhello.c -o hello.i

生成汇编代码

-S选项指示将程序编译成汇编语言,输出汇编源码,结果默认保存到.s文件。

$ gcc -S sayhello.c

创建静态库

静态库是一些.o文件的集合,管理工具为ar(archive,文档)。
hellofirst.c

#include <stdio.h>void hellofirst(void) {    printf("The first hello\n");}

hellosecond.c

#include <stdio.h>void hellosecond(void) {    printf("The second hello\n");}
$ gcc -c hellofirst.c hellosecond.c  // 编译成.o文件$ ar -r libhello.a hellofirst.o hellosecond.o // -r选项将.o目标文件插入到静态库(文档)。(如果文档中已存在此.o,就替换旧的)另外$ ar -xo libhello.a // -x选项将静态库展开成.o目标文件; -o选项指出展开时保留.o原始日期$ ar -t libhello.a // -t选项显示静态库包含的所有.o目标文件$ ar -v libhello.a // -v选项指出按照详细模式运行$ nm hellofirst.o  // nm工具可显示.o目标文件中的符号$ nm libhello.a    // nm工具可显示.a静态库中的符号        hellofirst.o:        00000000 T hellofirst                 U puts        hellosecond.o:        00000000 T hellosecond                 U puts$ strip -Nhellofirst libhello.a // strip工具可删除.a静态库中的符号,-N选项指定删除的符号名$ nm libhello.a    // 使用strip删除.a中符号信息后再次使用nm        hellofirst.o:                 U puts        hellosecond.o:        00000000 T hellosecond                 U puts$ objdump -S libhello.a  // objdump工具用于分析.o/.a/.so/ELF文件的信息,-S选项展开libhello.a的可执行汇编代码        In archive libhello.a:        hellofirst.o:     file format elf32-i386        Disassembly of section .text:        00000000 <.text>:           0:   55                      push   %ebp           1:   89 e5                   mov    %esp,%ebp           3:   83 ec 18                sub    $0x18,%esp           6:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)           d:   e8 fc ff ff ff          call   0xe          12:   c9                      leave          13:   c3                      ret          ......

使用静态库

在连接时定位库规则

1.大多数系统库保存在目录/lib、/usr/lib和/usr/local/lib中,因此会自动查找这些默认目录;2.通过使用一个或多个-L选项,可以指定查找其他目录;    $ gcc -L. -L/home/lib prog.o 3.先查找共享库再查找静态库;    $ gcc -lmilt prog.o // 先为libmilt.so查找每个系统库目录,然后是libmilt.a4.通过指定确切的库名可以限制所有的查找操作。    $ gcc libjj.a /home/libmilt.so prog.o // 连接时不在系统库目录查找libjj.a和libmilt.so

twohellos.c

void hellofirst(void);void hellosecond(void);int main(int argc, char *argv[]) {    hellofirst();    hellosecond();    return 0;}
$ gcc twohellos.c ./libhello.a -o twohellos  // 使用完整静态库名(lib*.a)限制连接时所有默认的查找操作或者$ gcc twohellos.c -L. -lhello -o twohellos  // 在-L指定的目录下查找-l指定的缩写静态库名(先找libhello.so,找不到后再找libhello.a)另外$ nm twohellos   // nm工具可显示ELF可执行文件中的符号        0804a020 B __bss_start        0804a020 b completed.6591        0804a018 D __data_start        0804a018 W data_start        ...        08048434 T hellofirst        08048448 T hellosecond        080482b0 T _init        ...        0804841d T main                 U puts@@GLIBC_2.0        08048390 t register_tm_clones        08048320 T _start        0804a020 D __TMC_END__        08048350 T __x86.get_pc_thunk.bx$ nm -D twohellos   // nm工具可显示ELF可执行文件中的符号,-D选项只显示动态符号                 w __gmon_start__        080484ec R _IO_stdin_used                 U __libc_start_main                 U puts

创建共享库

共享库也是一些.o文件的集合,其中每个地址(变量引用和函数调用)都是相对地址,允许在运行程序的时候,可以动态加载和执行共享库。
shellofirst.c

#include <stdio.h>void shellofirst(void) {    printf("The first hello from a shared library\n");}

shellosecond.c

#include <stdio.h>void shellosecond(void) {    printf("The second hello from a shared library\n");}
$ gcc -c -fpic shellofirst.c shellosecond.c // -c指出编译成.o文件;-fpic指出按照可重定位地址方式生成$ gcc -shared shellofirst.o shellosecond.o -o hello.so // -shared指出生成共享库或者$ gcc -fpic -shared shellofirst.c shellosecond.c -o hello.so另外$ nm -D hello.so   // nm工具可显示.so共享库中的符号,-D选项只显示动态符号        0000201c B __bss_start                 w __cxa_finalize        0000201c D _edata        00002020 B _end        000005a8 T _fini                 w __gmon_start__        000003c8 T _init                 w _ITM_deregisterTMCloneTable                 w _ITM_registerTMCloneTable                 w _Jv_RegisterClasses                 U puts        0000055b T shellofirst        00000581 T shellosecond$ strip hello.so   // strip工具可删除.so共享库中的符号$ nm hello.so      // 使用strip删除.so中符号信息后再次使用nm        nm: hello.so: no symbols$ ldd hello.so     // ldd工具可列出共享库hello.so的所有依赖关系    linux-gate.so.1 =>  (0xb77dd000)    libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7612000)    /lib/ld-linux.so.2 (0xb77de000)

使用共享库

要使用共享库,必须在运行时能够找到共享库的位置。因为是通过名字而不是目录定位共享库,因此可以在连接时使用一个共享库,而在运行时使用另一个共享库。(因此大多数共享库会把版本号作为名字的一部分。)

运行时载入共享库按如下顺序

1.UNIX程序采用ELF二进制文件格式,此格式动态段DT_RPATH中指出运行时动态库搜索目录,编译时使用GCC的“-Wl,-rpath”链接参数指定上述内容;2.环境变量LD_LIBRARY_PATH指定的动态库搜索目录;3.非文本文件/etc/ld.so.cache用于缓存共享库软连接列表,/etc/ld.so.conf文本文件中指定其他共享库搜索目录,由ldconfig工具维护;4.系统库目录/lib和/usr/lib。

ldconfig工具按如下顺序更新共享库列表

1.在/etc/ld.so.conf文件包含的目录(还有/lib和/usr/lib目录)中查找共享库;2.然后分析共享库的名字和内容,如果内容为软连接,将在/etc/ld.so.cache文件中重新缓存软连接的指向内容。3.将查找到的所有共享库列表更新到/etc/ld.so.cache文件中。$ sudo ldconfig    // 更新/etc/ld.so.cache文件(需要超级用户权限)$ sudo ldconfig -v // 更新/etc/ld.so.cache文件,-v选项显示详细信息$ ldconfig -p      // -p选项显示/etc/ld.so.cache文件中共享库软连接列表$ ldconfig -p | grep stdc++ // 在/etc/ld.so.cache文件中查找libstdc++.so    libstdc++.so.6 (libc6) => /usr/lib/i386-linux-gnu/libstdc++.so.6

stwohellos.c

void shellofirst(void);void shellosecond(void);int main(int argc, char *argv[]) {    shellofirst();    shellosecond();    return 0;}
$ gcc stwohellos.c hello.so -o stwohellos // 程序编译并连接到hello.so,但运行时需在系统库目录中定位到hello.so或者$ gcc stwohellos.c hello.so -Wl,-rpath=./ -o stwohellos // 程序编译并连接到hello.so,但运行时需在系统库目录或-Wl,-rpath选项指定的目录中定位到hello.so或者$ gcc stwohellos.c ./hello.so -o stwohellos// 程序编译并连接到hello.so,但运行时需在系统库目录或上述(./)目录中定位到hello.so另外$ nm -D stwohellos    // nm工具可显示ELF可执行文件中的符号,-D选项只显示动态符号        0804a024 B __bss_start        0804a024 D _edata        0804a028 B _end        08048614 T _fini                 w __gmon_start__        08048404 T _init        0804862c R _IO_stdin_used                 w _ITM_deregisterTMCloneTable                 w _ITM_registerTMCloneTable                 w _Jv_RegisterClasses                 U __libc_start_main                 U shellofirst$ ldd stwohellos      // ldd工具可列出ELF可执行文件的所有依赖关系        linux-gate.so.1 =>  (0xb7759000)        ./hello.so (0xb7753000)        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb758e000)        /lib/ld-linux.so.2 (0xb775a000)$ readelf -d stwohellos  // readelf工具可查看ELF文件信息,若编译时使用GCC-Wl,-rpath选项,可看到RPATH段信息。        Dynamic section at offset 0xf04 contains 26 entries:          Tag        Type                         Name/Value         0x00000001 (NEEDED)                     Shared library: [hello.so]         0x00000001 (NEEDED)                     Shared library: [libc.so.6]         0x0000000f (RPATH)                      Library rpath: [./]         0x0000000c (INIT)                       0x8048404            ...         0x6ffffffe (VERNEED)                    0x80483bc         0x6fffffff (VERNEEDNUM)                 1         0x6ffffff0 (VERSYM)                     0x80483a0         0x00000000 (NULL)                       0x0

从共享库中载入函数

可以不把共享库连接到程序里,而只是加载并执行共享库里的某些函数。dlopen()的第二个参数为RTLD_NOW时,它让共享库中的所有函数都载入内存;参数为RTLD_LAZY时,会推后每个函数的载入操作,直到它在调用dlsym()中被应用。另外,使用此函数GCC编译时需连接系统库-ldl
sayhello.c

#include <stdio.h>void sayhello(void) {    printf("Hello from a loaded function\n");}

saysomething.c

#include <stdio.h>void saysomething(char *string) {    printf("%s\n", string);}

say.c

#include <dlfcn.h>#include <stdio.h>#include <stdlib.h>#define err_exit(str) do { \    printf("%s\n", str); \    exit(EXIT_FAILURE); \    } while (0)int main(int argc, char *argv[]) {    void *handle;    void (*sayhello)(void);     void (*saysomething)(char *);    handle = dlopen("./libsayfn.so", RTLD_LAZY);    if (NULL != dlerror())        err_exit(dlerror());    sayhello = dlsym(handle, "sayhello");    saysomething = dlsym(handle, "saysomething");    sayhello();    saysomething("This is something");    dlclose(handle);        return 0;}
$ gcc -fpic -shared sayhello.c saysomething.c -o libsayfn.so  // 创建共享库libsayfn.so$ gcc say.c -ldl -o say  // 使用dlopen类似的函数需连接库-ldl

自动生成函数原型

gcc为编译C程序提供-aux-info选项,可以为.c文件中的函数自动生成原型.h

$ gcc stwohellos.c -aux-info stwohellos.h  // -aux-info选项后的参数为存放函数原型的文件名

编译C++程序

多源文件到可执行文件

speak.h

#include <iostream>class Speak {public:    void sayHello(const char *);};

speak.cpp

#include "speak.h"void Speak::sayHello(const char *str) {    std::cout << "Hello " << str << "\n";}

hellospeak.cpp

#include "speak.h"int main(int argc, char *argv[]) {    Speak speak;    speak.sayHello("world");    return 0;}
$ g++ hellospeak.cpp speak.cpp -o hellospeak // g++默认编译C++,自动连接到标准C++库(libstdc++.a)或者$ gcc hellospeak.cpp speak.cpp -lstdc++ -o hellospeak // gcc默认编译C,可通过-l选项使用标准C++库(libstdc++.a)编译

只进行预处理

-E选项指出,源代码只传递给预处理程序,结果默认显示在标准输出stdout上。

$ g++ -E speak.cpp -o speak.ii

生成汇编代码

-S选项指示将程序编译成汇编语言,输出汇编源码,结果默认保存到.s文件。

$ g++ -S speak.cpp

创建静态库

say.h

#include <iostream>void sayhello(void);class Say {private:     const char *string;public:    Say(const char *str) {        string = str;    }    void sayThis(const char *str) {        std::cout << str << " from a static library\n";    }    void sayString(void);};

say.cpp

#include "say.h"void Say::sayString(void) {    std::cout << string << "\n";}Say librarysay("Library instance of Say");

sayhello.cpp

#include "say.h"void sayhello(void) {    std::cout << "hello from a static library\n";}
$ g++ -c say.cpp sayhello.cpp$ ar -r libsay.a sayhello.o say.o

使用静态库

saymain.cpp

#include "say.h"int main(int argc, char *argv[]) {    extern Say librarysay;  // libsay.a库中对象的引用    Say localsay = Say("Local instance of Say");    sayhello();    librarysay.sayThis("howdy");    librarysay.sayString();    localsay.sayString();    return 0;}
$ g++ saymain.cpp libsay.a -o saymain等同于$ g++ saymain.cpp sayhello.cpp say.cpp -o saymain:~$ ./saymainhello from a static libraryhowdy from a static libraryLibrary instance of SayLocal instance of Say

创建共享库

GNU支持C++语言的一些扩展功能。例如所有系统头文件默认包含,就好像它们被封装在extern "C" {...}块中一样。
average.h

class Average {private:    int count;    double total;public:    Average(void) {        count = 0;        total = 0.0;    }    void insertValue(double value);    int getCount(void);    double getTotal(void);    double getAverage(void);};

average.cpp

#include "average.h"void Average::insertValue(double value) {    count++;    total += value;}int Average::getCount(void) {    return count;}double Average::getTotal(void) {    return total;}double Average::getAverage(void) {    return (total/(double)count);}
$ g++ -fpic -shared average.cpp -o average.so

使用共享库

showaverage.cpp

#include <iostream>#include "average.h"int main(int argc, char *argv[]) {    Average avg;    avg.insertValue(30.2);    avg.insertValue(88.8);    avg.insertValue(3.002);    avg.insertValue(11.0);    std::cout << "Average=" << avg.getAverage() << "\n";    return 0;}
$ g++ showaverage.cpp ./average.so -o showaverage:~$ ./showaverageAverage=33.2505

混合编译C和C++程序

C语言使用简单函数名,不考虑参数的个数和类型;而C++函数总将它的参数类型列表当作函数名的一部分。

在C++中调用C

csayhello.c

#include <stdio.h>void csayhello(const char *str) {    printf("%s\n", str);}

cpp2c.cpp

#include <iostream>extern "C" void csayhello(const char *str); // c++中需使用extern "C"声明C函数原型int main(int argc, char *argv[]) {    csayhello("Hello from cpp to c");    return 0;}
$ g++ -c cpp2c.cpp$ gcc -c csayhello.c$ gcc cpp2c.o csayhello.o -lstdc++ -o cpp2c或者$ gcc cpp2c.cpp csayhello.c -lstdc++ -o cpp2c错误方式如下$ g++ cpp2c.cpp csayhello.c -o cpp2c // 出错信息cpp2c.cpp:(.text+0x11): undefined reference to `csayhello'

在C中调用C++

cppsayhello.cpp

#include <iostream>extern "C" void cppsayhello(char *str); // c++中虽然用extern "C"指出为C函数,但实现在.cpp中,函数内部实际为C++代码void cppsayhello(char *str) {    std::cout << str << "\n";}

c2cpp.c

int main(int argc, char *argv[]) {    cppsayhello("Hello from C to C++");    return 0;}
$ gcc cppsayhello.cpp c2cpp.c -lstdc++ -o c2cpp错误方式如下$ g++ cppsayhello.cpp c2cpp.c -o c2cpp // 出错信息c2cpp.c:2:35: error: ‘cppsayhello’ was not declared in this scope

GCC: The Complete Reference——Arthur Griffith,胡恩华译

0 0