C语言学习高级004-C语言补充03

159 阅读6分钟

1、函数类型和函数指针:
用typedef定义一种函数类型,到时候用来定义变量指向一个具体的函数,然后调用变量就行。
typedef 返回值 (类型名)(参数类型列表) 比如 typedef void (p)(int)
然后有一个函数的类型是上面定义的类型,比如 void test(int age){};
然后在使用的时候:p *p1 = test; p1(20) 这样就行了, 【注意】:这里需要注意了,在后面定义的函数需要在文件前面先声明,否则使用的时候找不到函数报错了,不再是普通调用时候的警告了。

定义和调用方式总结:
1、1 typedef int(p)() ====》定义: p *p1 = 函数名;===》调用:p1()或者(*p1)()
1、2 typedef int(*p)()====>定义 p p1 = 函数名===》调用同上
1、3 不用typedef,int(*p)()= 函数名===》调用同上。

2、函数指针数组,定义一种函数类型指针的数组,初始化的时候数组里有多个函数名。这就是函数指针数组。 定义方法:
int (*p[])() = {函数名1,函数名2..};

【p后面的[]优先级高于前面的*,也就是p是一个数组,数组内容是指针,然后每个指针指向一个函数,函数返回值是int,参数空 】

前面的声明就是函数指针数组的意思

3、函数指针可以作为函数的参数实现函数的回调。

写法:void fun( int(*p)() ){},调用的时候就可以把其他同一类型的函数名传进去,在里面进行回调

4、预处理:编译之前,由程序预处理器对源代码进行处理。
文件包含命令#include
<>表示直接按照系统指定的目录查找,""先在当前目录查找,找不到再到系统指定目录查找
<>常用于包含系统的库文件,""常用于包含自定义的文件

一般用于头文件的包含

5、宏定义:
#define num 值,在该语句后面所有的语句中都将出现的num替换成了值,这就是宏定义
比如#defind PI 3.1415926,后面的就能直接使用PI了,编译器会自动转换成3.1415926这个值

【注意点】: 一般大写区别于变量、
可以是常数,也可以是表达式
不做语法检查,只有在宏展开后,也就是替换后,编译的时候会检查语法
不是C,不加分号
有效范围:从定义到文件结尾
可以用#undef 宏名 : 终止宏定义的作用域
宏定义中可以引用已经定义的宏名到值或者表达式中

宏定义本质就相当于mybaits中的$,直接替换,编译的时候再编译。

6、带参数的宏定义,上面是普通的宏定义
如果要把频繁使用的函数定义成宏,那么参数怎么处理,这里就涉及到带参数的宏 也就是宏函数,宏函数的创建, 宏函数名字中参数用括号括起来,不用类型,宏函数的宏的定义中整体也要用括号括起来,同时,定义体中用括号括住每一个参数。
例如 #define SUM(x,y) ((x)+(y)) ,调用的时候直接sum(10,20)

宏函数没有普通函数的压栈等开销,更效率高

一般用大写字母表示宏名,宏名中不能有空格的,如果只使用一次宏函数,效率没有太大的提高的

7、条件编译
根据条件只对一部分源代码编译,其他的代码不编译,默认所有的源代码都会编译的。
三种写法
第一种: #ifdef 宏名 第一行 #else 第二行 #endif

只要定义了宏名,不要宏体就行,就编译第一行,否则编译第二行 第二种:
#ifndef 宏名 第一行 #else 第二行 #endif

只要没有定义宏名,就编译第一行,否则编译第二行

第三种:
#if 宏值条件 第一行 #else 第二行 #endif
只要宏的值满足条件 就编译第一行,否则编译第二行。比如:#define M 10 ,可对M进行值的大小判断

7.1条件编译的应用:防止头文件重复引用
#ifndef _SOMEFILE_H
#define _SOMEFILE_H #endif

8、动态库的封装和使用:
库也就是能重复引用使用的代码的封装,不如公共的代码等,是一些目标文件的集合
windows平台上,最常用的c语言库是由集成按照开发环境所附带的运行库,这些库一般由编译厂商提供

8.1静态库的创建:创建空项目,创建头文件和源文件,项目属性-常规-项目默认值-配置类型,将配置类型的默认的exe改成静态库就行了,然后编译生成就行了。得到的.lib文件,将.lib文件和头文件给用户就能调用里面的函数了。

8.2静态库的引入使用:
方式一:将lib放在电脑任何地方,在c源文件中上面先 #pragma comment(lib,"lib文件路径") 这样就能直接调用lib中的函数了,注意,路径可以是绝对路径也可以是相对路径。

方式二:
右键工程-添加现有项-选择lib文件就行了

方式三:
1、先将头文件目录包含进来:右键工程=配置属性=c/c++=常规=附加包含目录 加上头文件的目录
2、将lib文件目录包含进来:右键工程=配置属性=连接器=常规=附加目录 编辑加上lib文件的目录
3、将lib文件名设置进来:右键工程=配置属性=连接器=输入=附加依赖项 编辑加上需要的lib名称

【注意】:vs生成的exe或者lib文件在最外层的debug文件夹,不是内存的debug文件夹。

静态库是被打包到整个程序中的,是绑定成一个整体的,打包完成后,原来的库文件可以删除了,但是这样就容易造成空间浪费。同时静态库的更新等问题也不方便,所有后来用到了动态库。

windows下动态库的使用:
1、动态库的创建:将上面静态库的配置属性改为动态库dll,同时将需要被外部程序调用的函数体和函数头文件的函数返回值前面加上 如下标志 __declspec(dllexport) 就可以了

生成有lib文件和dll文件,这里的lib文件和静态库的lib文件不同的。需要注意

编译的时候只会去链接lib文件,dll文件中的代码不会复制到程序中,只会在执行的时候才会去加载所需的dll文件,将dll映射到地址空间,然后去访问dll中的函数。

2、动态库的使用:
隐式调用:将动态库的头文件,dll文件和lib文件复制到程序的源代码目录下。然后在程序中使用:
#pragma comment(lib,"./mydll.lib")加载库,就行了。。

显式调用:
1)、头文件:<winsock2.h>
2)、加载dll,HMODULE hd = LoadLibrary("dll路径文件名")
3)、创建函数指针 type int (*Fun)()
4)、或者dll中的函数: Fun f1 = (Fun)GetProcAddress(hd,"函数名成");
5)、执行: f1();

【注意:】可能会出现一些错误,需要吧配置属性中的常规中的字符集的unicode字符集改为多字节字符集就行了。