复杂函数如何解读?
在C/C++开发中,经常会遇到非常复杂的函数声明比如:
int *(*(*pfn)(int *))[10];,这个到底是什么函数?看着一脸懵逼,,, 这边文章就带领大家,轻松读懂这种复杂函数。
在C语言中复杂函数有一个注明的解析法则:
右左解析法则:首先从变量名圆括号起,pfn先向右看,遇到圆括号,则调转方向,向左看*,这就说明pfn是一个指针。当括号内的内容解析完毕,就跳出括号,一直重复这个过程直到表达式解析完毕,其实复杂的函数离不开指针函数、函数指针、指针数组、数组指针。
解读复杂函数并不难,先从最简单的开始:
根据右左法则,直接解读出来的是,func 是一个指针,指向了一个函数(int),函数的返回值是int类型
int (*func)(int);
如下推导图所示:
再来看一个稍微复杂点的函数:
这个函数并不难,可以推导出:fun2 是一个数组,数组的元素存放指针,元素指向的是一个函数(int *),函数的返回值int
int (*fun2[5])(int *p);
如下示意图所示:
再来看一个如下代码:
根据右左法则:首先可以看到funn3是一个指针,指向了数组有5个元素[5] 所以fun3就是一个数组指针,数组中的元素类型是一个指针,指向了一个函数(int *p) 函数的返回值是int
int (*(*func3)[5])(int *p);
直接看下面的示意图就一目了然了:
再看一个复杂的函数:
func4 是一个指针,指向了一个函数(int *),返回值是一个指针,指针类型指向了一个数组[5] 返回值是一个数组指针,数组的元素存放int类型
int (*(*func4)(int *p))[5];
再来看 开篇的那个复杂的函数
pfn 是一个指针,指向了一个函数(int *),返回值是一个指针,指针的类型指向了一个数组[10],返回值是一个数组指针,数组的元素存放的int *
int *(*(*pfn)(int *))[10];
通过上述由易到难解读了复杂函数,以后在遇到复杂函数都可以轻松解读,依据右左法则进行解读。
函数中还需要注意:函数不能返回数组,而且数组中的元素也不能是函数类型。一般都会有指针来代替函数的返回数组类型,以及数组元素通过指针指向函数类型。
//C 语言不能直接返回一个数组,需要通过指针指向数组,返回指针即可
// int (*func6)(int)[5];
int (*(*func6)(int))[5];
//func5 是一个具有5个元素的数组,这个数组的元素都是函数,这是非法的,因为数组的元素除了类型必须一致,每个元素的所占空间也必须相同
//函数通常所占用的空间是不相同的,一般都是通过指针来指向函数。
// int func5[5](void);
int (*func5[5])(void);//指针数组 元素指向的是一个函数
IO 文件操作
C 语言 如何对文件进行操作,这也是需要掌握的技术点之一。
- 创建文件
C 创建的文件在.exe的同级目录下,因为C文件.c 编译 -> .o文件 -> 编译可执行的.exe文件
//c文件编译:.c -> .o -> .exe
int main1() {
FILE *p = fopen("./test/m.txt", "w");//打开文件
//r 只读,w 写入
if (p) {
fclose(p);//关闭文件
//这时候p就是悬空指针
p = NULL;
//文件存在
printf("文件存在");
} else {
//文件不存在
printf("文件不存在");
}
return 0;
}
- 读取文件 读取文件的目录必须和.exe文件在同级目录下
/**
* 读文件
*/
int main2() {
//读文件
FILE *p = fopen("./test/m.txt", "r");
if (p) {
while (1) {
char c = fgetc(p);
//如果读到的是EOF表示读取完毕
if (c == EOF) {
break;
}
printf("c:%c\n", c);//c:a
}
}
fclose(p);
p = NULL;
return 0;
}
- 写文件操作
/**
* 写文件
*/
int main3() {
FILE *p = fopen("./test/m.txt", "w");//打开文件
if (p) {
fputc('b', p);//全部覆盖重写
fclose(p);
p = NULL;
}
return 0;
}
- 复制文件的操作
/**
* copy文件
*/
int main() {
FILE *p = fopen("./test/write.txt", "w");//复制到新文件中
FILE *p1 = fopen("./test/m.txt", "r");//读取原文件
if (p == NULL || p1 == NULL) {
return -1;
}
while (1) {
char c = getc(p1);
if (c == EOF) {
break;
}
fputc(c, p);
}
fclose(p);
fclose(p1);
p = NULL;
p1 = NULL;
return 0;
}
- 解析文件内容 重点!!
在使用C做音视频开发的时候经常要解析音视频文件,这里需要掌握如何解析文件,先来解析一些简单的内容。
例如文件中有如下的内容,进行解析并计算结果
1+2=
4-2=
1*3=
6/2=
这里涉及到了通过fgets读取一行数据,sscanf进行格式化读取,一般单个字符读取通过EOF来判断结束,那么如果按照一行读取通过feof()函数来进行判断是否读取到最后一行
int acc(int a, char b,int c){
int result = 0;
switch (b) {
case '+':
result = a + c;
break;
case '-':
result = a - c;
break;
case '*':
result = a * c;
break;
case '/':
result = a / c;
break;
}
return result;
}
int main() {
FILE *p = fopen("./test/m1.txt", "r");
if (p) {
while (1) {
char buffer[100] = {0};//定义100个char,都设置为0,内存中是有脏数据的 推荐都设置为0
//读取一行 gets
fgets(buffer, sizeof(buffer), p);//将一行的数据 读取到buffer中
int a = 0;
char b = 0;
int c = 0;
//格式化读取
sscanf(buffer, "%d%c%d", &a, &b, &c);
printf("a:%d b:%c c:%d = %d\n",a,b,c, acc(a,b,c));
//判断文件结尾 通过feof判断文件是否读取到了末尾
if (feof(p)) {
break;
}
}
}
fclose(p);
p=NULL;
return 0;
}
执行的结果如下:
下面试着读取视频文件,代码如下:
#define BLOCK_SIZE (1024 * 64)
int main(int argc,char** argv){
//argc 参数长度 size
//argv[0] 被系统占用
//rb 读取二进制 wb 写二进制
printf("argc:%d agrv1:%s,agrv2:%s\n",argc,argv[1],argv[2]);//argc:1 agrv:(null)
FILE *pr1 = fopen(argv[1],"rb");
if(pr1 == NULL) return 0;
FILE *pr2 = fopen(argv[2],"wb");
if(pr2 == NULL) return 0;
//先设置缓冲区
struct stat st = {0};
//获取文件的大小
stat(argv[1],&st);
int size = st.st_size;//这个大小如何设置内不能说 视频100M 分配100M的内存 一定要进行判断
printf("size:%d\n",size);
if(size > BLOCK_SIZE){//如果大于64KB 则分块读取文件
size = BLOCK_SIZE;
}
char *buf = calloc(1,size);//calloc 声明一块内存空间 并且置为内存的初始值 malloc声明一块内存空间 不会置为0
unsigned int index = 0;//auto 自动正负号 unsigned正数
while (!feof(pr1)){
//读取文件 返回最终读取了多少个
int res = fread(buf,1,size,pr1);//读取pr1文件 读取1个size大小,读取到buffer中去
//写入目标文件
fwrite(buf,1,res,pr2);
index ++;
}
free(buf);
buf = NULL;
fclose(pr1);
fclose(pr2);
pr1 = NULL;
pr2 = NULL;
return 0;
}
在exe目录下输入一下的命令:cbase.exe ./test/input.flv ./test/output.flv 在test文件下存放input.flv文件,output.flv文件是生成的视频文件。