




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、.Linux静态and动态链接库创建和使用目 录1.概述42.静态库与动态库43.静态库的编写和使用43.1概述43.2编写最简单的静态库文件53.3制作库文件53.3.1生成目标文件53.3.2用ar命令归档,格式为ar -rc53.4使用库文件53.4.1编写一个测试程序main.c53.4.2编译目标文件53.4.3生成可执行文件63.4.4执行可执行文件查看效果64.动态库的编写和使用64.1概述64.2编写最简单的动态库文件64.3编译生成动态库 ,库文件名以lib开头, 以.so 结尾。74.4动态库的隐式调用74.4.1编写测试文件74.4.2编译测试程序,与静态库类似,要把头文
2、件的路径加到-I参数里面74.4.3连接生成测试程序74.4.4执行测试程序74.5使动态库被系统共享的三种办法84.5.1方法一:拷贝动态链接库到系统共享目录下,或在系统共享目录下为该动态链接库建立连接(硬连接或符号连接均可,常用符号连接)。84.5.2方法二:将动态链接库所在目录名追加到动态链接库配置文件/etc/ld.so.conf中。84.5.3方法三:利用动态链接库管理命令ldconfig,强制其搜索指定目录,并更新缓存文件,便于动态装入。84.5.4注意:84.5.5lconfig命令说明84.6动态库的显式调用94.6.1编写测试文件104.6.2编译测试文件 使用-ldl选项指
3、明生成的对象模块需要使用共享库104.6.3执行测试程序114.6.4显式调用例子程序2114.6.5相关函数的说明如下:124.7相关的命令134.7.1nm命令-查看库中的符号134.7.2ldd命令-检查程序都使用到哪些共享库134.8使用动态库时应注意的其他问题144.8.1动态库的路径设置144.8.2同名的静态和动态库的连接顺序141. 概述我们在实际编程工作中肯定会遇到这种情况:有几个项目里有一些函数模块的功能相同,实现代码也相同,也是我们所说的重复代码。比如,很多项目里都有一个用户验证的功能。代码段如下: /UserLogin.h文件,提供函数声明
4、60; int IsValidUser(char* username, int namelen); /UserLogin.c文件,实现对用户信息的验证 int IsValidUser(char* username, int namelen) int IsValid = 0; /*下面是具体的处理代码,略去*/ return IsValid
5、0; 如果每个项目都保存着这两个UserLogin.h和UserLogin.c文件,会有以下几个弊端:1、每个项目里都有重复的模块,造成代码重复。 2、代码的重用性不好,一旦IsValidUser的代码发生了变化,为了保持设计的一致性,我们还要手工修改其他项目里的UserLogin.c文件,既费时又费力,还容易出错。库文件就是对公共代码的一种组织形式。为了解决上面两个弊端,就提出了用库文件存放公共代码的解决方案,其要点就是把公共的(也就是可以被多次复用的)目标代码从项目中分离出来,统一存放到库文件中,项目要用到这些代码的时候,在编译或者运行的时候从库文件中取得目标代码即可
6、。库文件又分两种:静态库和动态库。2. 静态库与动态库如果程序是在编译时加载库文件的,就是使用了静态库。如果是在运行时加载目标代码,就成为动态库。换句话说,如果是使用静态库,则静态库代码在编译时就拷贝到了程序的代码段,程序的体积会膨胀。如果使用动态库,则程序中只保留库文件的名字和函数名,在运行时去查找库文件和函数体,程序的体积基本变化不大。静态库的原则是“以空间换时间”,增加程序体积,减少运行时间;动态库则是“以时间换空间”,增加了运行时间,但减少了程序本身的体积。3. 静态库的编写和使用3.1 概述 静态库文件的扩展名一般为.a,其编写步骤很简单。编写函数代码编译生成各目标文件用a
7、r文件对目标文件归档,生成静态库文件。注意归档文件名必须以lib打头。使用要点:在gcc 的-I参数后加上静态库头文件的路径。在gcc 的-L参数后加上库文件所在目录在gcc 的-l参数后加上库文件名,但是要去掉lib和.a扩展名。比如库文件名是libtest.a 那么参数就是 -l test3.2 编写最简单的静态库文件编写如下两个文件,注意放在同一目录中myalib.h /静态库头文件myalib.c /静态库实现文件 /myalib.h 文件的内容void test();/myalib.c 文件的
8、内容#inlcude void test() printf("testn");3.3 制作库文件3.3.1 生成目标文件gcc -c myalib.c 执行完后会生成一个myalib.o文件3.3.2 用ar命令归档,格式为ar -rc 再次提醒,归档文件名一定要以lib打头, .a结尾。 ar -rc libtest.a myalib.o 执行完后会生成一个libtest.a文件3.4 使用库文件 3.4.1 编写一个测试程序main.c内容为/main.
9、c 测试静态库调用的程序#include "myalib.h" /要把函数的头文件包含进来,否则编译时会报错 int main(int argc,char* argv) test(); return 0;3.4.2 编译目标文件注意:要把静态库头文件的路径加到-I参数里面gcc -I /root/exercise -o main.o -c main.c现在生成了一个main.o文件3.4.3 生成可执行文件注意:要把静态库文件的路径加到-L参数里面,把库文件名(去掉打头的lib和结尾的
10、.a)加到-l参数后面。如下面所示gcc -o main -L/root/exercise main.o ltest此时就会生成一个名为main的可执行文件另外,注意- l参数好象应该加到输入文件名的后面,否则会报错。3.4.4 执行可执行文件查看效果执行./main, 输出 test,说明执行成功。 4. 动态库的编写和使用4.1 概述 动态库一般以.so结尾,就是shared object的意思. 其基本生成步骤为1) 编写函数代码2) 编译生成动态库文件,要加上 -shared 和 -fp
11、ic 选项 ,库文件名以lib开头, 以.so 结尾。使用方式分为两种: 隐式调用和显式调用 :隐式调用类似于静态库的使用,但需修改动态链接库的配置文件/etc/ld.so.conf;显示调用则是在主程序里使用dlopen、dlsym、dlerror、dlclose等系统函数。生成动态库用gcc来完成,由于可能存在多个版本,因此通常指定版本号: $gcc -shared -Wl,-soname,libhello.so.1 -o libhello.so.1.0 hello.o 另外再建立两个符号连接: $ln -s libhello.so.1.0 libh
12、ello.so.1$ln -s libhello.so.1 libhello.so这样一个libhello的动态连接库就生成了。最重要的是传gcc -shared 参数使其生成是动态库而不是普通执行程序。 -Wl 表示后面的参数也就是-soname,libhello.so.1直接传给连接器ld进行处理。实际上,每一个库都有一个soname,当连接器发现它正在查找的程序库中有这样一个名称,连接器便会将soname嵌入连结中的二进制文件内,而不是它正在运行的实际文件名,在程序执行期间,程序会查找拥有 soname名字的文件,而不是库的文件名,换句话说,soname是库的区分标志。
13、0;这样做的目的主要是允许系统中多个版本的库文件共存,习惯上在命名库文件的时候通常与soname相同 。libxxxx.so.major.minor其中,xxxx是库的名字,major是主版本号,minor 是次版本号具体的调用方式会在 "五、动态库的调用" 中详细说明.4.2 编写最简单的动态库文件为了便于对照, 我们仍然采用静态库中的文件做例子。编写如下两个文件,注意放在同一目录中 myalib.h /静态库头文件myalib.c /静态库实现文件/myalib.h 文件的内容vo
14、id test();/myalib.c 文件的内容#inlcude void test() printf("testn");4.3 编译生成动态库 ,库文件名以lib开头, 以.so 结尾。gcc -fpic -shared -o libtest.so myalib.c此时就生成一个libtest.so文件4.4 动态库的隐式调用隐式调用的含义是代码里不出现库文件名,就是说这个代码和调用静态库的代码是类似的。4.4.1 编写测试文件/main.c 测试动态库隐式调用的程序#include "
15、;myalib.h" /要把函数的头文件包含进来,否则编译时会报错int main(int argc,char* argv) test(); return 0;4.4.2 编译测试程序,与静态库类似,要把头文件的路径加到-I参数里面gcc -I /root/exercise -o main.o -c main.c现在生成了一个main.o文件4.4.3 连接生成测试程序gcc -o main -L/root/exercise main.o ltest现在生成了一个main文件4.4.4 执行测试程序./main
16、60;此时出现提示./main: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory。 这个原因就是程序运行时并不知道动态库所在的路径,因此自然找不到。解决这个问题的办法有三种。见下节4.5 使动态库被系统共享的三种办法4.5.1 方法一:拷贝动态链接库到系统共享目录下,或在系统共享目录下为该动态链接库建立连接(硬连接或符号连接均可,常用符号连接)。这里说的系统共享目录,指的是LINUX动态链接库存放的目录,包括/lib
17、,/usr/lib以及/etc/ld.so.conf文件内所列的一系列目录.实例:执行# cp libtest.so /lib# ldconfig 或:# ln -s pwd/libtest.so /lib# ldconfig注意:pwd前后有两个反引号,其目的是取得pwd命令的输出,即当前目录.此时再执行main,即可成功.4.5.2 方法二:将动态链接库所在目录名追加到动态链接库配置文件/etc/ld.so.conf中。# pwd >> /etc/ld.so.conf # ldconfig 此时再执行main,即可成功.4.
18、5.3 方法三:利用动态链接库管理命令ldconfig,强制其搜索指定目录,并更新缓存文件,便于动态装入。# ldconfig pwd 此时再执行main,即可成功.4.5.4 注意:第三种方法虽然有效,但效果是暂时的,供程序测试还可以,一旦再度运行ldconfig,文件内容可能改变,所需的动态链接库可能不被系统共享了。而且无论哪种办法,其实质都是用ldconfig命令把动态库文件。所在路径加入到系统库列表中,(前两种永久,第三种临时) 。4.5.5 ldconfig命令说明ldconfig是一个动态链接库管理命令4.5.5.1 路径ldconfig在/sbin里面。4.5
19、.5.2 用途linux下的共享库机制采用了类似于高速缓存的机制,将库信息保存在/etc/ld.so.cache里边。程序连接的时候首先从这个文件里边查找,然后再到ld.so.conf的路径里边去详细找。这就是为什么修改了ld.so.conf要重新运行一下ldconfig的原因ldconfig 命令的用途,主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态 链接库(格式如前介绍,lib*.so*),进而创建出动态装入程序(ld.so)所需的连接和缓存文件.缓存文件默认为 /etc/ld.s
20、o.cache,此文件保存已排好序的动态链接库名字列表.4.5.5.3 用法ldconfig通常在系统启动时运行,而当用户安装了一个新的动态链接库时,就需要手工运行这个命令.ldconfig命令行用法如下:ldconfig -v|-verbose -n -N -X -f CONF -C CACHE -r ROOT -l -p|-print-cache -c FORMAT -format=FORMAT -V -?|-help|-usage
21、 path.ldconfig可用的选项说明如下:(1) -v或-verbose : 用此选项时,ldconfig将显示正在扫描的目录及搜索到的动态链接库,还有它所创建的连接的名字.(2) -n : 用此选项时,ldconfig仅扫描命令行指定的目录,不扫描默认目录(/lib,/usr/lib),也不扫描配置文件/etc/ld.so.conf所列的目录.(3) -N : 此选项指示ldconfig不重建缓存文件(/etc/ld.so.cache).若未用-X选项,ldconfig照常更新文件的连接
22、.(4) -X : 此选项指示ldconfig不更新文件的连接.若未用-N选项,则缓存文件正常更新.(5) -f CONF : 此选项指定动态链接库的配置文件为CONF,系统默认为/etc/ld.so.conf.(6) -C CACHE : 此选项指定生成的缓存文件为CACHE,系统默认的是/etc/ld.so.cache,此文件存放已排好序的可共享的动态链接库的列表.(7) -r ROOT : 此选项改变应用程序的根目录为ROOT(是调用ch
23、root函数实现的).选择此项时,系统默认的配置文件 /etc/ld.so.conf,实际对应的为 ROOT/etc/ld.so.conf.如用-r /usr/zzz时,打开配置文件 /etc/ld.so.conf时,实际打开的是/usr/zzz/etc/ld.so.conf文件.用此选项,可以大大增加动态链接库管理的灵活性.(8) -l : 通常情况下,ldconfig搜索动态链接库时将自动建立动态链接库的连接.选择此项时,将进入专家模式,需要手工设置连接.一般用户不用此项.(9) -p或-print-cache :
24、60;此选项指示ldconfig打印出当前缓存文件所保存的所有共享库的名字.(10) -c FORMAT 或 -format=FORMAT : 此选项用于指定缓存文件所使用的格式,共有三种: ld(老格式),new(新格式)和compat(兼容格式,此为默认格式).(11) -V : 此选项打印出ldconfig的版本信息,而后退出.(12) -? 或 -help 或 -usage : 这三个选项作用相同,都是让ldconfi
25、g打印出其帮助信息,而后退出.4.6 动态库的显式调用显式调用的含义是代码出现库文件名,用户需要自己去打开和管理库文件。其要点为:3) dlfcn.h系统头文件包含进来4) 用dlopen函数打开库文件,并指定打开方式5) 用dlerror()函数测试是否打开成功,并进行错误处理;6) 用dlsym获得函数地址,存放在一个函数指针中7) 用获得的函数指针进行函数调用。8) 程序结束时用dlclose关闭打开的动态库,防止资源泄露。9) 用ldconfig工具把动态库的路径加到系统库列表中4.6.1 编写测试文件/main.c 测试动态库显式调用的程序#include &
26、#160; /用于动态库管理的系统头文件 #include "myalib.h" /要把函数的头文件包含进来,否则编译时会报错 int main(int argc,char* argv) /声明对应的函数的函数指针 void (*pTest)(); /加载动态库 void *pdlHandle = dlopen("libtest.so", RTLD_LAZY);
27、60; /错误处理 if(pdlHandle = NULL ) printf("Failed load libraryn"); return -1; char* pszErr = dlerror();
28、160; if(pszErr != NULL) printf("%sn", pszErr); return -1; /获取函数的地址 pTest = dlsym(pdlHandle, "test&qu
29、ot;); pszErr = dlerror(); if(pszErr != NULL) printf("%sn", pszErr); dlclose(pdlHandle); return -1
30、; /实现函数调用 (*pTest)(); /程序结束时关闭动态库 dlclose(pdlHandle); return 0; 4.6.2 编译测试文件 使用-ldl选项指明生成的对象模块需要使用共享库gcc -o main -ldl main.c执行完后就生成了一个main文件4.6.3 执行测试程序执行 ./main 输出test 说明
31、成功。4.6.4 显式调用例子程序2/*FileName: main2.cDescription: test static/dynamic libraryAuthor: HCJDate : 2005-5-7*/#include<stdio.h>#include<dlfcn.h>int main(int argc, char* argv) /define function pointor int (*pStrlenFun)(char* pStr);
32、60; /声明对应的函数的函数指针 int (*pStrnlenFun)(char* pStr, int ulMaxLen); char str = "hello world" unsigned long ulLength = 0; void *pdlHandle; char *pszErr; pdlHandle = dlopen("./libstr.
33、so", RTLD_LAZY); /加载链接库/libstr.so if(!pdlHandle) printf("Failed load libraryn"); pszErr = dlerror(); if(pszErr != NULL) &
34、#160; printf("%sn", pszErr); return 0; /get function from lib pStrlenFun = dlsym(pdlHandle, "Strlen"); /获取函数的地址 pszErr = dlerror();
35、0; if(pszErr != NULL) printf("%sn", pszErr); return 0; pStrnlenFun = dlsym(pdlHandle, "StrNlen"); pszErr = dle
36、rror(); if(pszErr != NULL) printf("%sn", pszErr); return 0; printf("The string is : %sn", str); ulLength
37、 = pStrlenFun(str); /调用相关的函数 printf("The string length is : %d(use Strlen)n", ulLength); ulLength = pStrnlenFun(str, 10); printf("The string length is : %d(use StrNlen)n", ulLength); dlclose(pdlHandle); &
38、#160; return 0; gcc -o mian2 -ldl main2.c用gcc编译对应的源文件生成可执行文件,-ldl选项,表示生成的对象模块需要使用共享库。执行对应得文件同样可以得到正确的结果。4.6.5 相关函数的说明如下:4.6.5.1 dlopen()dlopen的第一个参数为共享库的名称,将会在下面位置查找指定的共享库。l 环境变量LD_LIBRARY_PATH列出的用分号间隔的所有目录。l 文件/etc/ld.so.cache中找到的库的列表,由ldconfig命令刷新。l 目录usr/lib。l 目录/lib。l 当前目录
39、。第二个参数为打开共享库的方式。有两个取值l RTLD_NOW:将共享库中的所有函数加载到内存l RTLD_LAZY:会推后共享库中的函数的加载操作,直到调用dlsym()时方加载某函数4.6.5.2 dlsym()调用dlsym时,利用dlopen()返回的共享库的phandle以及函数名称作为参数,返回要加载函数的入口地址。4.6.5.3 dlerror()该函数用于检查调用共享库的相关函数出现的错误。 这样我们就用简单的例子说明了在Linux下静态/动态库的创建和使用。 4.6.5.4 dlclose()关闭动态库.4.7 相关的命令4.7.1 nm命令-
40、查看库中的符号有时候可能需要查看一个库中到底有哪些函数,nm命令可以打印出库中的涉及到的所有符号。库既可以是静态的也可以是动态的。nm列出的符号有很多,常见的有三种,一种是在库中被调用,但并没有在库中定义(表明需要其他库支持),用U表示;一种是库中定义的函数,用T表示,这是最常见的;另外一种是所谓的“弱态”符号,它们虽然在库中被定义,但是可能被其他库中的同名符号覆盖,用W表示。例如,假设开发者希望知道上央提到的hello库中是否定义了 printf(): $nm libhello.so |grep printfU printfU表示符号printf被引用,但是并没有在函数内定义,由此
41、可以推断,要正常使用hello库,必须有其它库支持,再使用ldd命令查看hello依赖于哪些库: $ldd hello libc.so.6=>/lib/libc.so.6(0x400la000) /lib/ld-linux.so.2=>/lib/ld-linux.so.2 (0x40000000) 从上面的结果可以继续查看printf最终在哪里被定义,有兴趣可以go on 4.7.2 ldd命令-检查程序都使用到哪些共享库4.7.2.1 ldd命令行用法如下: ldd -version -v|-verbose -d|-da
42、ta-relocs -r|-function-relocs -help FILE. 各选项说明如下: (1) -version : 此选项用于打印出ldd的版本号. (2) -v 或 -verbose : 此选项指示ldd输出关于所依赖的动态链接库的尽可能详细的信息. (3) -d 或 -data-relocs : 此选项执行重定位,并且显示不存在的函数. (4) -r 或 -function-relocs : 此选项执行数据对象与函数的重定位,同时报告不存在的对象.
43、60; (5) -help : 此选项用于打印出ldd的帮助信息. 我们一般用-v选项.4.7.2.2 实例4.7.2.2.1 用静态库连接时的结果 #ldd mainlibc.so.6 => /lib/tls/libc.so.6 (0xb74ad000)/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb75eb000)可见使用静态库时,由于库已经被编译成程序的一部分,因此ldd的输出中就只有用到的系统库。4.7.2.2.2 用动态库隐式连接时的结果 li
44、btest.so => /root/exercise/libtest.so (0xb75e2000)libc.so.6 => /lib/tls/libc.so.6 (0xb74ab000)/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb75eb000)可见隐式使用动态库时,所有用到的动态库(包括系统和用户的)都会被显示出来。4.7.2.2.3 动态库显式连接时的结果ldd mainlibdl.so.2 => /lib/libdl.so.2 (0xb75e1000)libc.so.6 => /lib/tls/libc.so.
45、6 (0xb74aa000)/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb75eb000)可见显式使用动态库时,程序中不再保存运行时打开动态库的信息,只保留用到的系统库的信息. 这个与使用静态库时的输出是类似的.4.8 使用动态库时应注意的其他问题4.8.1 动态库的路径设置无论是动态库的显式调用还是隐式调用,都需要用ldconfig工具将动态库的路径加到系统库列表中,否则运行时会出错。 为了让执行程序顺利找到动态库,有三种方法: (1)把库拷贝到/usr/lib和/lib目录下。(2)在LD_LIBRARY_PA
46、TH环境变量中加上库所在路径。例如动态库libhello.so在/home/ting/lib目录下,以bash为例,使用命令:$export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ting/lib(3) 修改/etc/ld.so.conf文件,把库所在的路径加到文件末尾,并执行ldconfig刷新。这样,加入的目录下的所有库文件都可见。4.8.2 同名的静态和动态库的连接顺序当要使用静态的程序库时,连接器会找出程序所需的函数,然后将它们拷贝到执行文件,由于这种拷贝是完整的,所以一旦连接成功,静态程序库也就不再需要了。然而,对动态库而言,就不是这样。动态库
47、会在执行程序内留下一个标记指明当程序执行时,首先必须载入这个库。由于动态库节省空间,linux下进行连接的缺省操作是首先连接动态库,也就是说,如果同时存在静态和动态库,不特别指定的话,将与动态库相连接。4.8.2.1 例子现在假设有一个叫hello的程序开发包,它提供一个静态库libhello.a 一个动态库libhello.so,一个头文件hello.h,头文件中提供sayhello()这个函数 /* hello.h */void sayhello();另外还有一些说明文档。这一个典型的程序开发包结构 4.8.2.1.1 与动态库连接 linux默认的就是与动态
48、库连接,下面这段程序testlib.c使用hello库中的sayhello()函数 /*testlib.c*/#include#include int main() sayhello();return 0; 使用如下命令进行编译 $gcc -c testlib.c -o testlib.o 用如下命令连接: $gcc testlib.o -lhello -o testlib在连接时要注意,假设libhello.so 和libhello.a都在缺省的库搜索路径下/usr/lib下,如果在其它位置要加上-L参数 4.8
49、.2.1.2 与静态库连接与静态库连接与麻烦一些,主要是参数问题。还是上面的例子:$gcc testlib.o -o testlib -WI,-Bstatic -lhello 注:这个特别的"-WI,-Bstatic"参数,实际上是传给了连接器ld.指示它与静态库连接,如果系统中只有静态库当然就不需要这个参数了。如果要和多个库相连接,而每个库的连接方式不一样,比如上面的程序既要和libhello进行静态连接,又要和libbye进行动态连接,其命令应为: $gcc testlib.o -o testlib -WI,-Bstatic -lhello -WI,
50、-Bdynamic -lbye 5. 其他相关知识5.1 指针函数和函数指针的区别这两个概念都是简称,指针函数是指带指针的函数,即本质是一个函数。我们知道函数都又有返回类型(如果不返回值,则为无值型),只不过指针函数返回类型是某一类型的指针。5.2 函数指针5.2.1 概述函数指针是指向函数的指针变量。 因而“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。如前所述,C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可
51、引用其他类型变量一样,在这些概念上是一致的。函数指针有两个用途:调用函数和做函数的参数。5.2.2 方法函数指针的声明方法为: 数据类型标志符 (指针变量名) (形参列表); 注1:“函数类型”说明函数的返回类型,由于“()”的优先级高于“*”,所以指针变量名外的括号必不可少,后面的“形参列表”表示指针变量指向的函数所带的参数列表。例如: int func(int x); /* 声明一个函数 */ int (*f) (int x); /* 声明一个函数指针 */ f=func; /* 将func函数的首地址赋给指针f */ 赋值时函数func不带括号,也不带参数,由于func代表函数的首地址,
52、因此经过赋值以后,指针f就指向函数func(x)的代码的首地址。 注2:函数括号中的形参可有可无,视情况而定。 下面的程序说明了函数指针调用函数的方法: 5.2.3 例一 #include<stdio.h> int max(int x,int y) return(x>y?x:y); void main() int (*ptr)(int, int); int a,b,c; ptr=max; scanf("%d,%d",&a,&b); c=(*ptr)(a,b); printf("a=%d,b=%d,max=%d",a,b,
53、c); ptr是指向函数的指针变量,所以可把函数max()赋给ptr作为ptr的值,即把max()的入口地址赋给ptr,以后就可以用ptr来调用该函数,实际上ptr和max都指向同一个入口地址,不同就是ptr是一个指针变量,不像函数名称那样是死的,它可以指向任何函数,就看你想怎么做了。在程序中把哪个函数的地址赋给它,它就指向哪个函数。而后用指针变量调用它,因此可以先后指向不同的函数。不过注意,指向函数的指针变量没有+和-运算,用时要小心。 不过,在某些编译器中这是不能通过的。这个例子的补充如下。 应该是这样的: 1.定义函数指针类型: typedef int (*fun_ptr)(int,in
54、t); 2.申明变量,赋值: fun_ptr max_func=max; 也就是说,赋给函数指针的函数应该和函数指针所指的函数原型是一致的。 5.2.4 例二 #include<stdio.h> void FileFunc() printf("FileFuncn"); void EditFunc() printf("EditFuncn"); void main() typedef void (*funcp)(); funcp=FileFunc; funcp(); funcp=EditFunc; funcp(); 5.2.5 简单的函数指针的应
55、用。/形式1:返回类型(*函数名)(参数表) char (*pFun)(int); char glFun(int a) return; void main() pFun = glFun; (*pFun)(2); 第一行定义了一个指针变量pFun。首先我们根据前面提到的“形式1”认识到它是一个指向某种函数的指针,这种函数参数是一个int型,返回值是char类型。只有第一句我们还无法使用这个指针,因为我们还未对它进行赋值。
56、60; 第二行定义了一个函数glFun()。该函数正好是一个以int为参数返回char的函数。我们要从指针的层次上理解函数函数的函数名实际上就是一个指针,函数名指向该函数的代码在内存中的首地址。 然后就是可爱的main()函数了,它的第一句您应该看得懂了它将函数glFun的地址赋值给变量pFun。main()函数的第二句中“*pFun”显然是取pFun所指向地址的内容,当然也就是取出了函数glFun()的内容,然后给定参数为2。5.2.6 使用typedef更直观更方便。 /形式2:typedef 返回类型(*新类型)(参数表)typedef char (*PTRFUN)(int); PTRFUN pFun; ch
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 建材物流园工程可行性研究报告(参考)
- 国际冷链物流产业园扩建项目可行性研究报告(范文模板)
- 河南省开封市五县联考2023-2024学年高二上学期12月月考历史含解析
- 重庆第二师范学院《中级法语(二)》2023-2024学年第二学期期末试卷
- 平顶山学院《有机化学实验一》2023-2024学年第二学期期末试卷
- 广东茂名健康职业学院《节目策划通论》2023-2024学年第二学期期末试卷
- 四川信息职业技术学院《纳米工程导论》2023-2024学年第二学期期末试卷
- 湖南化工职业技术学院《体育赛事组织》2023-2024学年第二学期期末试卷
- 南阳科技职业学院《环境科学前沿》2023-2024学年第二学期期末试卷
- 贵州交通职业技术学院《网络与新媒体》2023-2024学年第二学期期末试卷
- 防火防爆技术课件:电气防爆
- 微笑曲线中文版课件
- 《古典决策理论》课件
- 2024年中考物理母题解密专题12 简单机械 机械效率考点精练(附答案)
- 观景台施工合同模板
- 存款代持协议书范文模板
- 标准化服务在博物馆展览策划中的应用考核试卷
- 2024年华东师大版学业水平信息技术模拟试卷(含答案解析)
- 派遣工的考勤管理制度
- GB/T 44353.1-2024动物源医疗器械第1部分:风险管理应用
- 中医培训课件:火龙罐的中医技术
评论
0/150
提交评论