1.常用的预处理指令
1.1 复习C语言中的指令,以‘#’开头,在预处理阶段中执行的指令
#include ...#define ...
#undef 取消宏定义
#if
#ifdef
#ifndef
#elif
#else
#endif
1.2 学习新的预处理指令
#line 整数n----------------------------------------将下一行的行号变成n,主要用于确保产生错误的行号保持不变
#warning 字符串---------------------------------主要用于在调试阶段产生警告信息
#error 字符串---------------------------------------主要用于在错误阶段产生错误信息
#pragma GCC dependency 文件名 -----------当前文件依赖于指定的文件名,如果指定文件的最后一次修改时间晚于当前文件,则产生警告信息 ;
#pragma GCC poison 标识符-------------------主要用于将指定的标识符设置为毒药,一旦使用则产生错误信息
#pragma pack(n)------------------------------------用于调整结构体的对齐和补齐方式为n的整数倍
主要便于存取数据的操作执行效率更高一些,而调整结构体的对齐和补齐方式主要为了节省内存空间。
注意:#if-----主要用于在编译链接阶段进行条件的判断
if -----主要用于可执行文件的阶段进行条件判断
1.3.预定义宏
__BASE_FILE__ %s
__FILE__ %s---------------主要用于获取当前宏所在的文件名称
__LINE__ %d--------------主要用于获取当前宏所在的行号
__FUNCTION__ %s
__DATE__ %s------------主要用于获取当前宏所在文件的最后一次编译变异的日期
__TIME__ %s-------------主要用于获取当前宏所在文件的最后一次编译变异的时间
2.环境变量的概念和使用
2.1 基本概念
环境变量一般是指在操作系统中用来指定操作系统运行环境/应用程序环境的一些变量
Path就是一个环境变量,一般来说,应用程序的执行需要带上路径才可以运行,而定义在环境变量Path中的路径系统会自动识别,因此对应的应用程序只需要程序名就可以运行
2.2 Unix/linux系统中的配置
echo 字符串
=> 字符串原样输出,回显
echo $SHELL
=> 获取SHELL的值,进行回显
echo $PATH
=> 获取PATH的值,进行回显打印
export PATH=$PATH:.
$PATH - 表示获取环境变量PATH中原来的数值
: - 多个路径之间的分隔符
. - 当前目录的路径
表示将当前目录的路径追加到PATH的环境变量值中
(一次性的,只是针对当前终端生效)
vi ~/.bashrc,打开文件,在文件的最后增加代码:
export PATH=$PATH:.
export PS1='\W$'
=> 表示终端的提示符只显示一个单词,使用$结尾
source ~/.bashrc
=> 使得配置文件立即生效
2.3 编程相关的环境变量
C_INCLUDE_PATH/CPATH --------- C头文件的附加搜索路径
CPLUS_INCLUDE_PATH ------------C++头文件的附加搜索路径
LIBRARY_PATH ------------------------- 链接库文件时查找的路径,主要解决编译时链接找不到库文件(即能放静态库又能放共享库)的问题(执行gcc时找不到库文件)
LD_LIBRARY_PATH ------------------- 运行时查找共享库的路径,主要解决运行时链接找不到库文件的问题(运行时找不到库文件)
2.4 头文件的查找方式
(1)#include <...>
表示去系统默认的路径中查找该头文件
/usr/include/stdio.h
(2)#include "..."
表示优先在当前工作目录下查找该头文件
(3)配置环境变量
export CPATH=$CPATH:.. => 一次性
(4)使用编译选项 -I(建议使用)
gcc/cc *.c -I 头文件的路径
gcc/cc *.c -I ..
3.库文件的概念和使用
3.1 库文件的概念
为了调用者使用的方便,一般并不会直接提供对应的.c文件或者.o文件,而是根据具体的功能模块,将对应的多个.o文件打包一个/多个库文件,给调用者提供库文件和头文件即可
库文件主要分成两种:静态库文件和共享库文件
静态库:本质上就是由若干个目标文件(.o)打包生成的.a文件
链接静态库的本质:就是将调用的代码指令复制到调用模块中,体现在最终的可执行文件中
共享库:本质上就是由若干个目标文件(.o)打包生成的.so文件
链接共享库的本质:并不是将被调用代码指令复制到调用模块中,而是将被调用代码指令在共享库中的相对地址复制到调用模块中,体现在最终的可执行文件中。
(1)静态库的特性
a.静态库在使用时,直接把代码 复制到 目标文件中
b.优点:不需要跳转,效率比较好;脱离静态库文件
c.缺点:目标文件会比较大;修改和维护都不太方便。占用空间比较大,最终生成的可执行文件比较大,库中代码一旦修改则需要重新链接才能体现在最终的可执行文件中
(2)共享库的特性(在主流的商业开发中,以共享库为主)
a.共享库在使用时,直接把代码对应的地址 复制过来
b.优点:目标文件比较小;修改和维护都比较方便
c.缺点:需要跳转,效率比较低;不能脱离共享库文件
(3)基本命令
ldd a.out => 表示查看a.out所链接的共享库信息
gcc/cc -static xx.c => 表示以静态库方式进行处理
比较发现,静态库方式生成的文件比较大
3.2 静态库的生成和使用步骤
(1)静态库的生成步骤
a.编写源代码(xxx.c文件)
vi add.c文件
b.只编译不链接生成目标文件(xxx.o文件)
gcc/cc -c add.c
c.生成静态库文件
ar -r/*插入*/ lib库名.a 目标文件
ar -r libadd.a add.o
注意:
静态库文件名的名字规则:以lib开头,以.a结尾
静态库文件名和库名是不同的概念,库名没前缀和后缀
(2)静态库的使用步骤
a.编写测试源代码(xxx.c)
vi main.c文件
b.只编译不链接生成目标文件(xxx.o)
gcc/cc -c main.c
c.链接测试文件和静态库文件,链接的方式有三种:
1)直接链接
cc main.o libadd.a
2)使用编译选项进行链接(掌握)
gcc/cc main.o -l 库名 -L 库文件所在的路径
gcc/cc main.o -l add -L .
3)配置环境变量LIBRARY_PATH
export LIBRARY_PATH=$LIBRARY_PATH:.
gcc/cc main.o -l add
3.3 共享库的生成和使用步骤
(1)共享库的生成步骤
a.编写源代码(xxx.c)
vi add.c文件
b.只编译不链接生成目标文件(xxx.o)
gcc/cc -c -fpic add.c /*-fpic 小模式,代码少,生成位置无关吗也就是生成相对地址*/
c.生成共享库文件
gcc/cc -shared 目标文件(xxx.o) -o lib库名.so
gcc/cc -shared/*共享*/ add.o -o libadd.so
(2)共享库的使用步骤
a.编写测试源代码(xxx.c)
vi main.c文件
b.只编译不链接生成目标文件(xxx.o)
gcc/cc -c main.c
c.链接测试文件和共享库文件,链接的方式有三种:
1)直接链接
cc main.o libadd.so
2)使用编译选项进行链接(掌握)
gcc/cc main.o -l 库名 -L 库文件所在的路径
gcc/cc main.o -l add -L .
3)配置环境变量LIBRARY_PATH(解决的是链接是找不到库文件的方式)
export LIBRARY_PATH=$LIBRARY_PATH:.
gcc/cc main.o -l add
注意:
(1)共享库的使用要求配置环境变量LD_LIBRARY_PATH的值,主要解决运行时找不到共享库的问题
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.(解决的是,运行时找不到库文件的方式)
(2)如果有一个静态库和一个共享库的库名相同,通过 gcc/cc main.o -l 库名 -L 库文件所在的路径 的方式链接,系统默认链接的是共享库,若想强制运行静态库则需要加上-static
3.4 共享库的动态加载
注意:编译链接时需要增加选项: -ldl
(1)dlopen函数
#include <dlfcn.h>
void *dlopen(const char *filename,int flag);
第一个参数:字符串形式的共享库文件名
第二个参数:标志
RTLD_LAZY - 延迟加载
RTLD_NOW - 立即加载
返回值:通用类型指针,
成功返回句柄,暂时理解为首地址,失败返回NULL
函数功能:主要用于打开和加载共享库文件
(2)dlerror函数
char *dlerror(void);
函数功能:
主要用于获取dlopen等函数调用过程发生的最近一个错误的详细信息,返回NULL则表示没有错误发生
(3)dlsym函数
void *dlsym(void *handle,const char *symbol);
第一个参数:句柄,也就是dlopen函数的返回值
第二个参数:字符串形式的符号,表示函数名
返回值:成功返回函数在内存中的地址,失败返回NULL
函数功能:
主要用于根据句柄和函数名获取在内存中的地址
(4)dlclose函数
int dlclose(void *handle);
函数功能:
主要用于关闭参数handle所指定的共享库,成功返回0,失败返回非0,当共享库不再被任何程序使用时,则回收共享库所占用的内存空间