我们之前讲了很多。但是指针的路还需要继续往下走,其实也快结束了,学习就是一种坚持的路,只有往前走才能学到更多,看到更多。(。・・。),今天的可能比前面的多,请耐心学习,谢谢
在指针更新完之后我会将指针的内容,整理发一片
这里是指针的快结束了,这里的一节结束还有最后有一片文章了,朋友们加油呀!
一,复习
1.1函数指针的写法
这里我们先发现一下函数指针,我们先来复习他的写法,我们使用一个简单的函数来复习他
int add(int x, int y)
{
return 0;
}
int main()
{
int (*padd)(int, int) = add;
//padd(4 ,5)
(*padd)(4 ,5)
add(4 , 5) //写法都是可以的
}
同时我们复习上节我们学习到的两个有趣的代码,两段有趣的代码来自《c陷阱和缺陷》这本书,下面的代码建议仔仔细细的看看
1.2两个代码的复习
((void ()())0)()
1.
我们来看看上面的代码,先将括号分清这里是这样的( (void(*)())0 ) ()
然后我们先分析最里面的void (*) ()
这是不是一个函数指针类型,为什么说是类型因为像int (*P)()这样才是一个函数指针的变量,没有P是一种类型———-这是最里面
接下来我们来看看(void()()0外面的()0是什么,我们学习过强制类型转换操作符()这里也是一样的使用方法是将整数强制类型转换成地址,如果我们在外面方一个那么他是不是解引用了所以*()0是解引用 —void(*)()我将它省略了方便看
最后(*()0)()像不像函数指针(*padd)()是像的,所以最依旧是一个函数的调用
2
void (signal(int , void()(int)))(int)
1.
我们来看看上面的代码,void ( *signal (int , void( *) (int) ) ) (int);
然后我们先分析最里面的signal (int , void( *) (int) ) 是不是像一个函数signal函数的参数有两个,一个是int 一个是函数指针,void( *) (int)函数时参数int 返回类型是void
最外面的void () (int )是函数指针,指针指向的函数时参数是int 返回类型是void
1.3数组指针
int (*p) [10] = &arr;
| | |
| | |
| | p指向数组的元素个数
| p是数组指针变量名
p指向的数组的元素类型
先看p1;首先当我们的p1与【】在一起时先和【】结合,所以p1[10]是数组,然后加上*就构成了我们的指针数组
其这次p2;为了防止p2与【】在一起时先和【】结合,我们将使用()将p2和【】隔开;所以p2不是指针数组
P1是数组,数组有10个元素,每个元素的类型是int*
P2是指针,指针指向的是数组,数组有10个元素,每个元素的类型是int,p2是数组指针
二,函数指针数组
2.1函数指针数组
我们说过字符数组,整型数组,那么函数数组怎么写呢?我们可以在写代码中去写,例如我们写函数实现加减乘除
#define _CRT_SECURE_NO_WARNINGS 1
#include
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
我们创造了四给函数去实现我们的加减乘除的运算,如果我们想要调用他们那么怎么实现呢?
#include
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
int main()
{
int (*pa[4])(int, int) = { add,sub,mul,div };
int i = 0;
for (i = 0; i
在这个时候我们就要将我们的加减乘除的函数放到我们的函数数组中间例如我们写下面的代码
函数指针数组
int (*pa[4])(int,int)={add,sub,mul,div}; //函数指针数组
我们就使用了上面的代码完成了函数服务器托管网指针数组,在我们这个完成的后,我们就可以实现模拟计算机加减乘除。这个代码我们用简单的方式去实现的。
2.2模拟计算机加减乘除
#define _CRT_SECURE_NO_WARNINGS 1
#include
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
do
{
printf("*************************n");
printf(" 1:add 2:sub n");
printf(" 3:mul 4:div n");
printf(" 0:exit n");
printf("*************************n");
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = add(x, y);
printf("ret = %dn", ret);
break;
case 2:
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = sub(x, y);
printf("ret = %dn", ret);
break;
case 3:
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = mul(x, y);
printf("ret = %dn", ret);
break;
case 4:
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = div(x, y);
printf("ret = %dn", ret);
break;
case 0:
printf("退出程序n");
break;
default:
printf("选择错误n");
break;
}
} while (input);
return 0;
}
三,回调函数
3.1,回调函数介绍
就是⼀个通过函数指针调⽤的函数。
如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数
时,被调⽤的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条
件发⽣时由另外的⼀⽅调⽤的,⽤于对该事件或条件进⾏响应。
第13讲中我们写的计算机的实现的代码中,红⾊框中的代码是重复出现的,其中虽然执⾏计算的逻辑
是区别的,但是输⼊输出操作是冗余的,有没有办法,简化⼀些呢?
因为红⾊框中的代码,只有调⽤函数的逻辑是有差异的,我们可以把调⽤的函数的地址以参数的形式
传递过去,使⽤函数指针接收,函数指针指向什么函数就调⽤什么函数,这⾥其实使⽤的就是回调函
数的功能。
3.2,回调函数应用
这里我们将我们刚刚使用过的计算机实现,进行优化,怎么优化?,我们先思考如果我们将实现计算机的函数增加那么switch函数中间会越来越长,而且我们所写的函数,指针数组的内容也会越来越长,那我们怎么去优化这个呢?
3.2.1使用回调函数优化代码
例如
case 1:
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = add(x, y);
printf("ret = %dn", ret);
break;
case 2:
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = sub(x, y);
printf("ret = %dn", ret);
break;
case 3:
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = mul(x, y);
printf("ret = %dn", ret);
break;
case 4:
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = div(x, y);
printf("ret = %dn", ret);
break;
他们有许多相似的地方。而相似的地方太多,又会显得我们代码有些冗余。
那么我们是否能够将相同的部分合并成一个函数?然后我们让这个函数去完成计算。我们可以创建一个函数例如cuil;我们将我们函数的地址传给他,这里我们使用函数指针去去实现
void cuil(int(*pa)(int ,int ))
{
}
////我们构建了函数
//代码相同的地方
//printf("输⼊操作数:");
// scanf("%d %d", &x, &y);
// printf("ret = %dn", ret);
//代码不同的地方
// et = add(x, y);
我们将相同的地方写入函数。然后,将不同的地方以传地址的方式传给我们的函数,所以这里我们使用了指针函数。下面是函数内部
void cuil(int(*pa)(int ,int ))
{
int x, y;
int ret = 0;
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = pa(x, y);
printf("ret = %dn", ret);
}
根据我们构建函数的知识,我们就可以对我们计算机函数的代码进行简单的优化,我们就可以得到下面面这样一段代码。
#include
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
void cuil(int(*pa)(int ,int))
{
int x, y;
int input = 1;
int ret = 0;
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = pa(x, y);
printf("ret = %dn", ret);
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
do
{
printf("*************************n");
printf(" 1:add 2:sub n");
printf(" 3:mul 4:div n");
printf(" 0:exit n");
printf("*************************n");
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
cuil(add);
break;
case 2:
cuil(sub);
break;
case 3:
cuil(mul);
break;
case 4:
服务器托管网 cuil(div);
break;
case 0:
printf("退出程序n");
break;
default:
printf("选择错误n");
break;
}
} while (input);
return 0;
}
3.3两个代码和对比
|
|
3.4总结
函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数
时,被调⽤的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条
件发⽣时由另外的⼀⽅调⽤的,⽤于对该事件或条件进⾏响应。
即我们没有直接使用这个函数,而是通过别的函数调用,跟前我们的函数嵌套相似但是不同
四. qsort使⽤举例
4.1,复习冒泡排序
#define _CRT_SECURE_NO_WARNINGS 1
#include
void bubble_sort(int arr[],int se)
{
int i = 0;
for (i = 0; i arr[j + 1])
{
tem = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tem;
}
}
}
}
int main()
{
int arr[10] = { 0};
int se = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i
像我们这样的冒泡排序有什么缺点呢?一眼就能看到这种排列方式,只能用来排列整型所以在这里我们就要介绍一个新的函数qsort:
4.2qsort介绍
qsort的实现结构
void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));
指向比较两个元素的函数的指针。
int (*compar)(const void*p1,const void*p2)
将两个指针作为参数(均转换为const void*)。该函数通过返回(以稳定和传递的方式)来定义元素的顺序:
Return Value |
Description |
p1 less than p2 |
|
0 |
p1 equivalent to p2 |
> 0 |
p1 greater than p2 |
上面的表格我们使用代码来看
/* qsort example */
#include /* printf */
#include /* qsort */
int values[] = { 40, 10, 100, 90, 20, 25 };
int compare(const void* a, const void* b)
{
return (*(int*)a - *(int*)b);
}
int main()
{
int n;
qsort(values, 6, sizeof(int), compare);
for (n = 0; n
然后我想讲的是记住他的那个表格。和他的结构就行了。(〜∇)〜
4.2.1qsort总结
4.3 使⽤qsort函数排序整型数据
当我们学会使用这个过函数的时候,我们就使用这个简单的库函数去排列简单的整形排练,(ノ^o^)ノ
#include
#include
//int cmp_int(const void*p1, const void*p2)是用来比较p1和p2指向元素的大小
int cmp_int(const void* p1, const void* p2)
{
return *(int*)p1 -*(int*)p2; //void*不能直接解引用,这里强制类型转化
}
void print_arr(int arr[],int se)
{
int i = 0;
for (i = 0; i
4.3.1使用qaort排列结构体
struct Stu //学⽣
{
char name[20];//名字
int age;//年龄
};
//假设按照年龄来⽐较
int cmp_stu_by_age(const void* e1, const void* e2)
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
//strcmp - 是库函数,是专⻔⽤来⽐较两个字符串的⼤⼩的
//假设按照名字来⽐较
int cmp_stu_by_name(const void* e1, const void* e2)
{
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
//按照年龄来排序
void test2()
{
struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
//按照名字来排序
void test3()
{
struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}
int main()
{
test2();
test3();
return 0;
}
4.4,qsort函数的模拟实现
使⽤回调函数,模拟实现qsort(采⽤冒泡的⽅式)。
注意:这⾥第⼀次使⽤ void* 的指针,讲解 void* 的作⽤
#include
int int_cmp(const void * p1, const void * p2)
{
return (*( int *)p1 - *(int *) p2);
}
void _swap(void *p1, void * p2, int size)
{
int i = 0;
for (i = 0; i 0)
{
_swap(( char *)base + j*size, (char *)base + (j + 1)*size, size);
}
}
}
}
int main()
{
int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
for (i = 0; i
4.4.1库函数strcmp
Value |
Relationship of string1 to string2 |
string1 less than string2 |
|
0 |
string1 identical to string2 |
> 0 |
string1 greater than string2 |
他的头文件是
字符串之间是无法直接比较大小的,在这个时候我们就使用库函数strcmp
4.5,qsort深入了解。
我们在以前应该讲过算法中的冒泡排序是一种很简单的方法。
qsort是一个库函数可以直接使用,它的底层的实现是使用快速排序的算法,对我们的数据进行排序,算法不需要去了解,你要了解这个函数的作用。\(・`(ェ)・)/
我们先写一个冒泡排序
#define _CRT_SECURE_NO_WARNINGS 1
#include
void bubble_sort(int arr[],int se)
{
int i = 0;
for (i = 0; i arr[j + 1])
{
tem = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tem;
}
}
}
}
int main()
{
int arr[10] = { 0};
int se = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i
那我们通过冒泡排序就可以去比较整型的大小。然后我们就可以通过我们的qsort的去优化的。
在优化之前,我们先更了解一下qsort
(1)qsort是一个可以直接使用,他的实现是依靠快速排序
(2)void qsort( void *base, //待排序数组的起始位置
size_t num, //待排序数组的元素个数
size_t width, //待排序数组的元素大小
int
(__cdecl *compare )(const void
*elem1, const void *elem2
) ); //函数指针
//这个函数指针指向一个函数
这个函数是用来比较待排序数组的两个元素的,p1指向一个元素,p2指向一个元素
4.6优化思路
当我们了解了这个函数,我们在上面所写的都是排列整型之间的顺序。那么接下来我们就要学习去排列任意数据类型之间的顺序。
我们整理一下思路。(这里我们是以冒泡排序的方式去优化我们的代码)
1,我们以前的函数参数都是int,但是当我们要去实现所有的类型排序。那么参数也需要改变。
void buusort(int arr[],int //仅仅使用int类型的仅仅只能够接收int类型指针
2,假设我们排10个元素。无论是整型还是其他的数据类型,他都只需要相同的趟数。所以我们以前冒泡排序的趟数是不需要改变的。
int se =sizeof(arr)/sizeof(arr[0]); //这里我们列举10个元素的比较
3 ,那我们依旧实现的是前后两个数的比较。那么对于这种比较,对于我们所写的循环的第二循环就不需要改变
int j = 0;
for (j = 0; j arr[j + 1]) //从小到大
{
int tem = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tem;
}
}
4,比较的地方要改造我们的回调函数。在我们以前的排序中,我们是直接创造了一个整型,通过整形局交换变量,
int tem = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tem;
通过以上的思路,我们就要去模拟实现我们这个qsort
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
void swap(char* bulf1,char* bulf2,size_t width)
{
int i = 0;
for (i = 0; i 0) //从小到大
{
//int tem = arr[j];
//arr[j] = arr[j + 1];
//arr[j + 1] = tem;
swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
}
}
}
}
int cmp(const void* p1, const void* p2)
{
return *(int*)p1 -*(int*)p2; //void*不能直接解引用,这里强制类型转化
}
void print_arr(int arr[],int se)
{
int i = 0;
for (i = 0; i
相当于照葫芦画瓢,大家可以尝试模拟qsort去排列我们的结构体;
五, sizeof和strlen
在之前的时候,我们说过sizeof和strlen的区别,今天我们依旧再说一边sizeof和strlen的区别,我们从下面的来自开始分析我们的
5.1,sizeof
在学习操作符的时候,我们学习了 sizeof , sizeof 计算变量所占内存内存空间⼤⼩的,单位是
字节,如果操作数是类型的话,计算的是使⽤类型创建的变量所占内存空间的⼤⼩。
sizeof 只关注占⽤内存空间的⼤⼩,不在乎内存中存放什么数据。
#inculde
int main()
{
int a = 10;
printf("%dn", sizeof(a));
printf("%dn", sizeof a);
printf("%dn", sizeof(int));
return 0;
}
5.2,strlen
strlen 是C语⾔库函数,功能是求字符串⻓度。函数原型如下
size_t strlen ( const char * str );
统计的是从 strlen 函数的参数 str 中这个地址开始向后, 之前字符串中字符的个数。
strlen 函数会⼀直向后找 字符,直到找到为⽌,所以可能存在越界查找。
#include
int main()
{
char arr1[3] = {'a', 'b', 'c'};
char arr2[] = "abc";
printf("%dn", strlen(arr1));
printf("%dn", strlen(arr2));
printf("%dn", sizeof(arr1));
printf("%dn", sizeof(arr1));
return 0;
}
5.3,总结
sizeof——不挑类型
.sizeof是操作符
sizeof计算操作数所占内存的⼤⼩,
单位是字节
不关注内存中存放什么数据
strlen——-对于字符串的
strlen是库函数,使⽤需要包含头⽂件 string.h
srtlen是求字符串⻓度的,统计的是 之前字符的隔个数
关注内存中是否有 ,如果没有 ,就会持续往后找,可
能会越界
于是到今天为止。我们指针终于快学完了。
最后我会根据指针去找一些题目。在后面单独发标出来。拜拜~~~\(・`(ェ)・)/
希望大家能和我一起努力学习,加油,使用陶渊明的诗词
人生无根蒂,飘如陌上尘。分散逐风转,此已非常身。落地为兄弟,何必骨肉亲!
得欢当作乐,斗酒聚比邻。盛年不重来,一日难再晨。及时当勉励,岁月不待人。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
平时的工作和生活中,我们常常会将照片、视频、文档等数据保存在电脑硬盘上,并且在处理数据的时候,有时候会遇到误删除文件的情况。误删除重要文件会带来不必要的麻烦和损失,但是文件丢失不太容易避免,因此,懂得如何找回删除的文件这项电脑技巧还是非常有必要的。本文将和大家…