1、Unix系统下 C 编程,北京神州数码思特奇信息技术股份有限公司 研究院,示例,C程序设有一个文件名为hello.c的程序,其内容为:,编译和执行步骤,编译gcc hello.c #生成可执行程序a.out,或 gcc o hello hello.c #生成可执行程序hello 运行./a.out 或./hello 输出结果Hello World!,纲 要,编译器 头文件 链接器与库文件 静态库 共享库,功能及用法,为什么要使用编译器? C语言源程序需要经过编译和链接这两个过程才能转换成二进制可执行程序。 一般在Unix系统中使用的C编译器是cc(C Compiler的缩写)。在各个Linux
2、发行版本中广泛使用的C编译器名为gcc(GNU cc)。为了保持与Unix系统的兼容,在Linux系统中cc作为了gcc的一个链接。,gcc的功能,gcc能将C/C+源程序和目标程序编译并调用链接程序ld生成可执行文件,如果用户没有给出可执行文件的名字,gcc将默认生成一个名为a.out的可执行文件。,gcc的用法,gcc的一般用法为: gcc options 其常用格式为:gcc -c-S-E-s-g-static-shared-rdynamic-Idir -Ldir -lmylib -Olevel-x Language -Dmacro=defn -Umacro-mmachine-optio
3、n -o out_file infile ,几点说明,gcc根据源程序的扩展名来决定使用哪一种语言的编译器进行编译工作。 扩展名为“.c”的文件被gcc认为是C语言的源程序文件。例如:gcc hello.c gcc编译出来的可执行程序默认是a.out。 g+是一个C+版本的gcc编译器。 g+要求C+语言源程序文件带有扩展名“.cc”或“.C”。例如:g+ hello.cc, g+ hello.C,参数,-c:编译后仅输出*.o型的目标文件,而不连接生成可执行程序 -S:编译后仅生成汇编语言文件*.s,但不生成目标文件和可执行代码 -s:生成可执行文件时,删除符号表和重定位信息。生成成品软件时
4、使用 -E:在预处理过程后结束,不进行编译和连接,也不生成可执行代码 -g:在可执行文件中加入调试信息,便于程序的调试,参数(续),-o outfile:指定输出文件名。若不指定则生成a.out -Idir:将目录dir添加到头文件搜索范围 -lmylib:连接时搜索库libmylib.a -Ldir:将目录dir添加到库文件搜索范围 -OL:编译时进行优化。L为优化级别,分别03和s。生成最终产品时使用 -static:禁止使用共享库(动态连接库) -shared:生成共享库 -rdynamic:连接时使用共享库,gcc的常用选项,gcc的常用选项,c+程序,一个c+版的Hello Worl
5、d程序,其文件名为hello.C,内容为:,编译方法,使用c+或g+来编译: g+ hello.C #生成可执行程序a.out c+ o hello hell.C #生成可执行程序hello g+ s -o Hello hello.C #生成删除符号表的可执行程序Hello 或使用gcc并指定库文件来编译c+程序: gcc c hello.C #生成目标文件hello.o gcc o h hello.C lstdc+ #指定标准c+库,生成可执行程序h,编译多个文件,目录结构(1) 编译命令$ gcc my_app.c greeting.c o my_app 目录结构(2) 编译方式(1)$
6、gcc my_app.c functions/greeting.c o my_app -I function,编译多个文件,目录结构(2) 编译方式(2) 分步编译 命令: 1、$gcc -c my_app.c -Ifunctions 2、$gcc -c functions/greeting.c 3、$gcc my_app.o greeting.o o my_app 思路: 编译每一个.c文件,得到.o的目标文件; 将每一个.o的目标文件链接成一个可执行的文件;,编译多个文件,gcc的工作过程,使用gcc/g+由C源代码文件生成可执行文件的过程,有以下四个阶段预处理(也称预编译,Preproc
7、essing) 编译(Compilation) 汇编(Assembly) 链接(Linking),头文件,在标准C中有两种形式的头文件使用方式: #include #include “headfile.h”区别: #include 型头文件搜索范围为默认位置/usr/include, #include“headfile.h”型头文件的搜索位置为当前目录,在Linux的GNU C中,若当前目标不存在headerfile.h,则也会到默认位置去搜索。,链接器与库文件,UNIX/Linux的链接器为ld,其功能是将目标文件或库文件链接在一起,生成可执行文件,一般在编译过程的最后执行。Linux标准库
8、文件一般存放在目录/lib或/usr/lib。默认情况下链接器查找C语言的标准库函数。如果使用的不是标准的库函数,必须通过-llib或-Llibdir告诉链接器ld,否则将无法找到库函数。,关于库文件的规定,库文件命名必须遵守一定命名规则,库文件名字必须永远以lib开头,后紧跟库类名,文件名的后缀为 .a:传统静态库 .so:共享库或动态链接库例如,libc.a为标准C库,libm.a为数学运算静态库,libc.so.6和libm.so.6分别为标准C和数学运算共享库。,静态库,静态库也叫档案(archive),以.a为后缀,用于编译链接后生成静态可执行文件。用户可以使用库管理程序ar和ran
9、lib来创建和管理自己的或已有的静态库。,引例 设有C语言文件f1.c,f2.c,f3.c,它们的内容分别为:,/文件f1.c的内容 f1(int arg) printf(”F1: you passed: %dn”,arg); /文件f2.c的内容: f2(char *arg) printf(”F2: you passed: %sn”,arg); ,/文件f3.c的内容#include main() fprintf(stderr,”Begine:n”);f1(15); f2(”Hello World!”);fprintf(stderr,”:Endn”);exit(0); ,程序举例,编译与链接
10、,可以采用各模块文件分别编译然后再统一链接的办法进 行编译。cc c f1.c f2.c /生成f1.o和f2.o cc o f f3.c f1.o f2.o /生成f cc o fp f3.c f1.c f2.c /生成fp cc c f1.c f2.c f3.c /生成f1.o,f2.o和f3.o cc o myp f1.o f2.o f3.o /生成myp,构造和管理静态库,用户可以使用命令ar构造自己的静态库:cc -c f1.c f2.c #生成目标文件f1.o和f2.oar crv libmyl.a f1.o f2.o #生成库libmyl.aranlib libmyl.a #为子
11、函数建立索引表,说明,ar用于静态库文件的管理,其功能是库创建、修改和从库中取出模块等 ranlib用于为刚建立的库文件建立索引表,通过索引表可以加快库文件搜索速度。其用法为:ranlib -vV ar_file,使用自己的库,#使用库libmyl.a和f3.c生成可执行程序fp: cc -o fp f3.c libmyl.a #使用库libmyl.a和f3.o生成可执行程序fp: cc -o fp f3.o libmyl.a#-L指定当前目录,-lmyl指定静态库文件libmyl.a: cc -o fp f3.c -L. lmyl,共享库,Linux系统的另一种库文件为共享库,用于生成动态链
12、接的可执行程序。 共享库文件名的格式为:libNAME.so.N NAME为库名,N为版本号。可用命令ldd和ldconfig命令管理共享库。,构造共享库,共享库构造非常简单,只需要在构造库的时候使用-shared参数就可以了。 例如:用f1.c和f2.c构造共享库,方法是:#生成目标文件 cc -c f1.c f2.c #由目标文件生成共享库 cc -shared -o libmy.so f1.o f2.o #由源文件生成共享库 cc -shared -o libmy.so -c f1.c f2.c,共享库的使用,共享库要使用头文件dlfcn.h和几个相关的函数:dlerror dlopen
13、 dlsym dlclose。,1. dlopen,功能: 用于打开指定共享库,并返回文件描述符。 原型及用法: void *dlopen(const char *filename, int flag); 返回值: 成功时返回文件描述符,否则返回NULL。,dlopen函数的相关说明,变量filename为共享库名。若文件名不以/开头,则为非绝对路径名,将按以下顺序搜索库文件:(1)环境变量中的LD_LIBRARY_PATH值指定的路径;(2)动态链接缓冲文件/etc/ld.so.cache;(3)库文件默认目录/lib,/usr/lib。 变量flag用来表示在什么时候解决未定义的符号,其取
14、值范围与意义如下: (1)RTLD_LAZY:指定在动态链接库的函数执行时解决; (2)RTLD_NOW:指定在dlopen返回前就解决所有未定义的符号问题。一旦有未解决好未定义的符号,dlopen将返回NULL表示错误。注意:RTLD_LAZY和RTLD_NOW可以与RTLD_GLOBAL配合使用,使得那些在以后才加载的库可以获得其中的符号。,2. dlsym,功能:返回共享中指定函数的入口地址 原型及用法: void *dlsym(void *handle, char *symbol); 说明: dlsym根据共享库文件描述符(handle)与符号(symbol),返回symbol对应的(
15、函数)入口地址,相当于返回一个(函数)指针。,3. dlclose,功能: 关闭已经打开的指定共享库文件,此操作应在共享库相关操作完成之后进行。 原型及用法: int dlclose (void *handle);,4. dlerror,功能: 返回动态共享库操作状态信息。当共享库操作函数执行失败时,dlerror可以返回出错信息,否则返回值为NULL表示成功。原型及用法为: const char *dlerror(void);,共享库使用示例,为了使用刚创建的共享库,需要对引例中的模块文件f3.c进行修改。假定修改后的文件被命名为f3n.c,其代码如下:,#include #include
16、#define SO_FILE “./libmy.so“ main()void *sfp; char *err;int tmpi=16;int (*f1) (int ),(*f2)(char *); /定义函数指针sfp=dlopen(SO_FILE,RTLD_LAZY); /打开共享库if(sfp=NULL) fprintf(stderr,dlerror(); exit(1); f1=dlsym(sfp,“f1“); /获取函数f1入口地址(指针)err=dlerror(); /检查是否成功if(err) fprintf(stderr,err); exit(2);,共享库使用示例,f2=dl
17、sym(sfp,“f2“); /获取函数f2入口地址err=dlerror(); /检查是否成功if(err) fprintf(stderr,err); exit(3); fprintf(stderr,“-begine-n“);f2(“Test String“); /调用函数f2f1(tmpi); /调用函数f1fprintf(stderr,“+end+n“);dlclose(sfp); /关闭共享库exit(0); ,共享库使用示例,编译方法,编译方法为: cc o myp f3n.c ldl由共享库libmy.so生成可执行程序myp,-ldl则指示链接程序ld使用 dl函数库。在编译时也
18、可以使用-rdynamic参数,告诉链接程序在链接时所有函数均使用共享库。其方法为: cc rdynamic o myp f3n.c ldl,共享库的管理,ldd (2) ldconfig,(1) ldd,功能: ldd(Library Dependency Display)用来显示一个可执行程序或共享库所使用的共享库间的依赖关系。例如: ldd myp ldd /usr/bin/mesg,(2)ldconfig,功能: 在默认目录(/lib和/usr/lib)或动态库配置文件/etc/ld.so.conf内所列的目录下或指定目录下搜索共享库,进而创建出动态装入程序ld.so所需的链接和缓存文
19、件。 缓存文件默认为/etc/ld.so.cache,其中保存有已排好序的由/etc/ld.so.conf指定的目录内动态链接库名字列表。 为了让系统或用户动态链接库为系统所共享,需要运行ldconfig来对共享库进行配置。 ldconfig通常在系统启动时运行,而当用户安装了一个新的动态链接库时,就需要手工运行这个命令。用法为: ldconfig 选项 ,ldconfig的使用示例,1) 显示搜索的目录和共享库,并更新缓存文件/etc/ld.so.cacheldconfig -v默认情况下,ldconfig不输出任何东西。使用-v参数可以显示正在扫描的目录及搜索到的共享库。2) 安装共享后,更新目录/lib内的符号链接ldconfig n /lib,