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,胡恩华译
- linux gcc 编译 c
- gcc编译C语言
- gcc编译c问题
- gcc 编译C
- GCC编译C/C++
- gcc 编译.c文件
- Gcc编译C/C++程序
- 利用GCC编译obj-c
- linux gcc编译C程序
- gcc编译c文件指导
- gcc编译c语言程序
- C/C++编译过程(gcc)
- linux gcc编译C程序
- linux gcc编译C程序
- Gvim、Gcc、cvim编译c
- gcc命令行下面编译C
- gcc编译链接C代码
- GCC编译C/C++代码
- 折叠菜单
- 从零开始学JDBC--1.9 代码抽取--使用Properties读取配置文件
- 扩展欧几里得 UVA
- JAVA进阶----ThreadPoolExecutor机制
- 我认识的京缘
- GCC编译C/C++
- DFS算法
- CSS3 帧动画(Sprite,直译叫雪碧图)
- 通过 Docker 化一个博客网站来开启我们的 Docker 之旅
- java集合架构____HashSet、LinkedHashSet、TreeSet
- sql优化,js,数据库 适合面试者看的一些要点
- 路由器开发(二)—— 路由器工作原理
- Android Native Crash崩溃及错误原因
- linux mysql5.5安装与配置