g++编译过程和动态连接库

简介

gcc :GNU编译器套件(GNU Compiler Collection)是到了4.7,才真正支持c11的。
gcc & g++如今是gnu中最主要和最流行的c & c++编译器 。
g++是将默认语言设为c++,连接时自动使用C++标准库而不用c标准库 。
C++标准库:http://www.runoob.com/cplusplus/cpp-standard-library.htmlhtml

1、编译过程

  1. 预编译过程:处理宏定义和include,去除注释,不会对语法进行检查,生成.i文件:命令:g++ -E Test.cpp > Test.i
  2. 编译过程:检查语法,生成汇编指令.s文件;[编译器egcs];命令:g++ -S Test.cpp -o Test.s
  3. 汇编过程:汇编变为目标代码(机器代码)生成.o的文件 [汇编器as];命令:g++ -c Test.cpp
  4. 连接过程:生成可执行程序[连接器ld];命令:g++ Test.o -L F:\vs2008\VC\include\iostream

2、编译器参数

  • -std=c++11 指定语言标准,只有编译C或C++时才有用,除了c++11,还有c90、c8九、c99等等

调试选项 -g和-pglinux

  • -g :生成能被GNU调试器使用的调试信息;用level指出须要多少信息,默认的level值是2
  • -pg:在编译好的程序里加入额外的代码。运行程序时, 产生gprof用的剖析信息以显示你的程序的耗时状况。

优化选项 -O(0~3) 优化编译、连接,-O0表示没有优化,-O1为缺省值,-O3优化级别最高;ios

  • -O:进行跳转和延迟退栈两种优化
  • -O2:除了完成-O1,还进行额外的调整工做,如指令调整??
  • -O3:包括循环展开和其余一些与处理特性相关的优化工做??
  • 可是一般状况下,自动的东西都不是太聪明,太大的优化级别可能会使生成的文件产生一系列的bug。通常可选择2;3会有必定风险。

目录选项c++

  • -I:用来指定头文件目录;-Idirname:将dirname所指出的目录加入到程序头文件目录列表中,是在预编译过程当中使用的参数;/usr/include目录通常是不用指定的,若是不加你会获得一个"xxxx.h: No such file or directory"的错误,gcc xxx.c -lm(动态数学库,库名m) -lpthread(连接多线程库) -ldl(连接dl库)
    库文件放在/lib和/usr/lib和/usr/local/lib里的库直接用-l参数就能连接,而放在其余目录,则要使用-L紧跟库文件所在目录;#include< > 引用的是编译器的类库路径里面的头文件。 #include" " 引用的是你程序目录的相对路径中的头文件。-l和-L:指定程序要连接的库,紧接库名;

错误与警告选项多线程

  • -v:列出全部编译步骤
  • -w:关闭全部警告信息
  • -Wall:打印出gcc提供的警告信息
  • -Wextra:打印出更多的警告信息,比开启 -Wall 打印的还多
  • -Werror:要求GCC将全部的警告当成错误进行处理,在警告发生时停止编译过程。
  • -Wno-sign-compare: 关闭当有符号转换为无符号时,有符号和无符号值比较产生的错误警告。
  • -Wno-unused-local-typedefs:忽略本地未使用的类型定义警告。
  • -Wno-deprecated-declarations:关闭使用废弃API的警告.

连接方式函数

  • -static:禁止使用动态库。优势:程序运行不依赖于其余库。缺点:可执行文件比较大。
  • -shared:此选项将尽可能使用动态库,为默认选项。优势:生成文件比较小。缺点:运行时须要系统提供动态库。 须要配合参数-fPIC使用
  • -symbolic :创建共享目标文件的时候,把引用绑定到全局符号上。对全部没法解析的引用做出警告(除非用链接选项,'-Xlinker -z -Xlinker defs'取代)。只有部分系统支持该选项。
  • -Wl,-Bstatic:告诉连接器ld只连接静态库,若是只存在动态连接库,则连接器报错。
  • -Wl,-Bdynamic:告诉连接器ld优先使用动态连接库,若是只存在静态连接库,则使用静态连接库。
  • -m32为生成32位的动态连接库。 -m64位生成64位的动态连接库。

其余选项工具

  • -fpic:编译器就生成位置无关目标码.适用于共享库(shared library)
  • -fPIC:编译器就输出位置无关目标码.适用于动态链接(dynamic linking)。
  • -pipe:使用管道代替编译中临时文件,在使用非gnu汇编工具的时候,可能有些问题
  • -msse:让编译器使用cpu的sse指令集,可使用mmx寄存器计算单精度浮点运算。
  • -msse2:让编译器使用奔腾cpu的指令集,可使用mmx寄存器计算双精度浮点运算。
  • -mno-mmx-mno-sse-mno-sse2:不使用MMX,SSE,SSE2指令。
  • -fpermissive:把代码的语法错误做为警告,并继续编译。请谨慎使用该选项。
  • -rdynamic:用来通知连接器将全部符号添加到动态符号表中,程序动态调用动态库中的函数,编译时用到该选项,一般和-ldl一块儿用。
  • -D name=def: 加入宏定义,若不指定def,则默认为1
  • //一些进行缓冲区溢出实验时可能须要的选项
  • -fstack-protector-fno-stack-protector:是否开启堆栈保护,这里的保护是在返回地址以前加入一个验证值来确保返回地址不被破坏
  • -z execstack :启用可执行栈,默认是禁用的

3、生成动态连接库

在实际开发过程当中,各个模块之间会涉及到一些通用的功能,好比读写文件,查找、排序。为了减小代码的冗余,提升代码的质量,能够将这些通用的部分提取出来,作出公共的模块库。经过动态连接库能够实现多个模块之间共享公共的函数。优化

建立动态库,经过shared和fPIC编译参数生产so动态连接库文件。程序在调用库函数时,只须要链接上这个库便可。spa

/*caculate.h*/
#ifndef CACULATE_HEAD
#define CACULATE_HEAD
int add(int a, int b);
int sub(int a, int b);
int div(int a, int b);
int mul(int a, int b);
#endif

/*caculate.c文件*/
#include "caculate.h"
extern "C"{
int add(int a, int b)
{ 
  return (a + b);
}
int sub(int a, int b)
{
  return (a - b);
}
int div(int a, int b)
{ 
  return (int)(a / b);
} 
int mul(int a, int b)
{ 
  return (a * b); 
}
}

编译生产libcac.so文件以下插件

g++ -fPIC -shared -o libxxx.so xx1.cpp xx2.cpp xx3.cpp  //多个文件成成一个动态链接库

g++ -fPIC -shared caculate.cpp -o libcac.so

4、调用动态库

#include <stdio.h>
#include "caculate.h"
int main(){ 
  int a = 20; 
  int b = 10; 
  printf("%d + %d = %d\n", a, b, add(a, b));
  printf("%d - %d = %d\n", a, b, sub(a, b));
  printf("%d / %d = %d\n", a, b, div(a, b));
  printf("%d * %d = %d\n", a, b, mul(a, b)); 
  return 0;
}

编译生产可执行文件main以下: g++ main.c -o main -L./ -lcac

5、动态获取动态连接库的函数

linux提供dlopen、dlsym、dlerror和dlcolose函数获取动态连接库的函数。
经过这个四个函数能够实现一个插件程序,方便程序的扩展和维护。

#include <dlfcn.h> 以指定模式打开指定的动态连接库文件,并返回一个句柄给 dlsym()的调用进程

void *dlopen(const char *filename, int flag);

char *dlerror(void);

void *dlsym(void *handle, const char *symbol);

int dlclose(void *handle);//使用dlclose()来卸载打开的库。 Link with -ldl.

  • dlopen 该函数将打开一个新库,并把它装入内存。该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的。

flag在linux 下有三种解析方式 RTLD_LAZY 暂缓决定,等有须要时再解出符号  RTLD_NOW 当即决定,返回前解除全部未决定的符号。

做用范围,可与解析方式经过“|”组合使用。
RTLD_GLOBAL:动态库中定义的符号可被其后打开的其它库解析。
RTLD_LOCAL: 与RTLD_GLOBAL做用相反,动态库中定义的符号不能被其后打开的其它库重定位。若是没有指明是RTLD_GLOBAL仍是RTLD_LOCAL,则缺省为RTLD_LOCAL。

handle = dlopen(full_path.c_str(), RTLD_LAZY | RTLD_GLOBAL);

  • dlsym 根据动态连接库操做句柄与符号,返回符号对应的地址,不但能够获取函数地址,也能够获取变量地址。

void _dlsym(void_handle,const char*symbol)

handle:由dlopen打开动态连接库后返回的指针;

symbol:要求获取的函数或全局变量的名称。

  • dlclose函数负责关闭指定句柄的动态库,当该库的使用计数为0时,就会被卸载
#include <stdio.h> 
#include <dlfcn.h>

#define DLL_FILE_NAME "libcac.so" 
typedef int (CAC_FUNC)(int, int); 
//using function = void()(int, int);
//c++11 
int main() { 
void *handle; 
CAC_FUNC func = NULL; 
char *error; 
int a = 30; 
int b = 5;
handle = dlopen(DLL_FILE_NAME, RTLD_NOW);
if(handle == NULL)
{
    fprintf(stderr, "Failed to open libaray %s error:%s\n", DLL_FILE_NAME, dlerror());
    return -1;
}
*(void **) (&func) = dlsym(handle, "add");
if(func == NULL){
    printf("ERROR:%s:dlsym\n", dlerror());
    return -1;
}
printf("%d + %d = %d\n", a, b, func(a, b));

func = (CAC_FUNC)dlsym(handle, "sub");
printf("%d + %d = %d\n", a, b, func(a, b));

func= (CAC_FUNC)dlsym(handle, "div");
printf("%d + %d = %d\n", a, b, func(a, b));

func = (CAC_FUNC)dlsym(handle, "mul");
printf("%d + %d = %d\n", a, b, func(a, b));

dlclose(handle);
return 0;
}

输出:g++ maindl.cpp -o maindl -ldl

30 + 5 = 35 30 + 5 = 25 30 + 5 = 6 30 + 5 = 150

void *dlsym(void _handle, const char _symbol); 返回值为void (void )&(func)是将函数指针的地址强制转换void类型,而后使用取值,获取dlsym的返回值 实际这个地方没有必要这样,函数指针原本就是地址,能够直接用 func = dlsym(handle, "add");

  • void 和 void*

一、函数的返回值若是是 void 类型的,则表示这个函数的执行结果是没有返回值的; 二、函数的返回值若是是 void* 类型的,则表示返回一个内存地址,这个内存空间存放的数据类型是void类型的,即无类型的,也能够说是万能型的 三、在实际开发中,void* 使用时最终都要强制转换成某种明确的数据类型

相关文章
相关标签/搜索