当前位置:网站首页>C语言指针(特别篇)
C语言指针(特别篇)
2022-07-07 06:26:00 【一只大喵咪1201】
作者:一只大喵咪1201
专栏:《C语言学习》
格言:你只管努力,剩下的交给时间!
在本喵前面的文章《C语言(上、中、下)》三篇文章中由浅入深详细的讲解了指针的类型以及用法,基于这三篇文章的基础上,特别提出一个概念——回调函数。
回调函数
概念:
- 回调函数是通过函数指针调用一个函数。把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
注意
- 回调函数不是由该函数的实现方直接调用,也就是不通过函数名直接使用。
- 是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
库函数qsort
有一个int类型的数组,我们将其实现升序排列。
#include <stdio.h>
void bubble_sort(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz - 1; i++)
{
int j = 0;
int flag = 1;
for (j = 0; j < sz - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
flag = 0;//只要发生交换就将标志位写0
}
}
//判断是否完成交换
if (flag)
{
break;
}
}
}
int main()
{
int arr[] = {
9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
//冒泡法升序排列
bubble_sort(arr, sz);
//打印数组中的元素
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
通过冒泡排序的方法,实现了将原本降序排列的数组重排成升序排列的数组。
在C语言的库函数中,有一个排序函数供我们使用,那就是qsort函数。
我们来看一下它的定义:
这是它的函数声明以及需要引用的头文件。
这是对qsort函数形参的介绍。
- qsort函数是通过快排的方式实现排序的
void qsort(void* base, size_t num, size_t width, int(__cdecl* compare)(const void* elem1, const void* elem2));
- 参数分别是
- 要排数据的起始地址
- 要排数据的个数
- 要排数据中每个数据的大小
- 排序的方式
排序方式是由用户自己定义的,将比较方式写成一个函数,再将函数的地址作为实参传给qsort函数就可以使用。所以它有一个形参的类型是函数指针变量类型,用于接收比较函数的地址。
int(__cdecl* compare)(const void* elem1, const void* elem2)
- 该函数指针指向的函数中
- 俩个形参是const void*类型的指针变量类型
- 返回类型是int类型,也就是指针变量elem1和emel2指向的俩个数的差值
如果elem1指向的数大于elem2指向的数,则返回一个大于0的数。
如果elem1指向的数等于elem2指向的数,则返回一个0.
如果elem1指向的数小于elem2指向的数,则返回一个小于0的数。- 其中elem1指向的是需要排序数据中的前一个数,elem2指向的是需要排序数据中的后一个数。
那么形参中const void*类型的指针变量又是什么意思呢?
void*类型的指针变量
const只是为了起修饰作用,将指针变量修饰成一个常变量,使得 * emel1或者 * emel2无法通过解引用来修改其中的值。
void* 类型的指针是一种无类型的指针
当需要一个指针变量,但又暂时不知道它是什么类型的指针变量的时候,就可以将其写成一个void * 类型的指针。
在知道了需要的指针变量类型后,就可以通过强制类型转换将void * 类型的指针变量转换成我们需要的。
如
(int*)emel1
就可以将void * 类型的指针变量emel1转换成为int * 类型的指针变量。
我们使用qsort函数实现上面的数组的升序排序。
#include <stdio.h>
#include <stdlib.h>
int int_cmp(const void* e1, const void* e2)
{
return (*(int*)e1 - *(int*)e2);
}
int main()
{
int arr[] = {
9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
int width = sizeof(arr[0]);
qsort(arr, sz, width, int_cmp);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
与我们使用冒泡排序的结果相同。
多种类型数据的排序
有没有想过,为什么用户自己定义的比较函数中形参类型的指针是void * 类型的呢?
qsort库函是的作者在写的时候肯定是不知道用户要比较的数据类型是什么,所以就暂且设置成一个void * 类型的指针变量,当用户在自己写比较函数的时候,将void * 类的指针变量转换成自己需要的即可。
qsort函数是可以排序任意类型的数据的
下面举一些例子:
- char类型数据的排序
#include <stdio.h>
#include <stdlib.h>
int char_cmp(const void* e1, const void* e2)
{
return (*((char*)e2) - *((char*)e1));
}
int main()
{
char arr[] = "abcdef";
int sz = sizeof(arr) / sizeof(arr[0]) - 1;//字符结束标志'\0'不需要排序
int width = sizeof(arr[0]);
qsort(arr, sz, width, char_cmp);
printf("%s\n", arr);
return 0;
}
将字符串按照ASCII码值的大小降序排列。
- 结构体类型数据的排列
#include <stdio.h>
#include <stdlib.h>
struct stu
{
char name[20];
int age;
};
int struct_cmp_age(const void* e1, const void* e2)
{
return (((struct stu*)e1)->age - ((struct stu*)e2)->age);//转变后的指针必须用括号括起来
}
int main()
{
struct stu s[3] = {
{
"zhangsan",18},{
"lisi",25},{
"wangwu",21} };
int sz = sizeof(s) / sizeof(s[0]);
int width = sizeof(s[0]);
qsort(s, sz, width, struct_cmp_age);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%s,%d\n", s[i].name, s[i].age);
}
return 0;
}
按照年龄从小到大将三个人排序。
将比较方式修改为按照名字比较后
int struct_cmp_name(const void* e1, const void* e2)
{
return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
}
程序的其他内容不动。
按照首字母的ASCII码值升序将三个人排序。
模拟实现qsort函数
我们在上面自己写了一个冒泡排序的程序,接下来我们利用冒泡排序的是思想实现一个qsort库函数功能的函数my_qsort函数。
参照着库函数qsort的函数声明
void qsort(void* base, size_t num, size_t width, int(__cdecl* compare)(const void* elem1, const void* elem2));
以整型数组升序排列为例:
#include <stdio.h>
//由于不知道传过来的数据类型是什么,所以先写成void*类型的指针变量
void Swap(char* buf1,char* buf2,int width)
{
int i = 0;
//按对进行交换
for (i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
void my_qsort(void* base, int sz, int width, int (*pf)(const void*, const void*))
{
int i = 0;
for (i = 0; i < sz - 1; i++)
{
int j = 0;
int flag = 1;
for (j = 0; j < sz - 1 - i; j++)
{
//char*类型的指针变量加上数据宽度就得出每个数据的起始地址
if (pf((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
//将俩个数据的起始地址传给交换函数
Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
flag = 0;
}
}
if (flag)
{
break;
}
}
}
int int_cmp(const void* e1, const void* e2)
{
return (*(int*)e1 - *(int*)e2);
}
int main()
{
int arr[] = {
9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
int width = sizeof(arr[0]);
my_qsort(arr, sz, width, int_cmp);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
主要的难点在于my_qsort函数的实现。
- 由于并不知道要排序的数据类型是什么,所以要写成void * 类型
- 在函数内部,由于void * 类型的指针变量是无法解引用以及使用的,所以将其转化成为最小单位的指针变量char * 类型的。
当知道需要排序的数组是int类型后,在最小单位的基础上加上一个域宽就可以得到下一个数据的首地址。
数组中的元素如上图那样存在内存中。
- 将数组名当作实参传递个my_qsort函数后,开始进行冒泡排序
- 当调用比较函数的时候
- 将数组中俩个int类型数据的首地址,也就是图中(char*)base + j * width和(char*)base + (j + 1) * width)俩个地址传递给比较函数,该函数是通过函数指针调用的。
- 将比较后的结果进行判断后,如果需要交换,将(char*)base + j * width和(char*)base + (j + 1) * width)俩个地址传递给Swap交换函数。
- 在Swap函数中,通过域宽width,也就是int数据类型的大小控制着循环的进行。
- 在循环中将俩组数据成对交换,直到实现两个int类型数据的交换。
- 直到实现了所有数据的交换。
如此一来我们就实现了qsort功能的函数,并且使用的是冒泡排序的思想,而不是快排的思想。
多种类型数据的排序
像库函数qsort那样,我们自己写的my_qosrt函数也可以实现不同数据类型的实现,在上面写my_qsort函数的过程中已经排列了int类型的数据,接下来我是排序一下别的数据类型。
- char类型数据的排序
int char_cmp(const void* e1, const void* e2)
{
return (*(char*)e2 - *(char*)e1);
}
int main()
{
char arr[] = "abcdef";
int sz = sizeof(arr) / sizeof(arr[0]);
int width = sizeof(arr[0]);
my_qsort(arr, sz, width, char_cmp);
printf("%s\n", arr);
return 0;
}
与使用库函数qsort的结果一样。
- 结构体类型数据排序
struct stu
{
char name[20];
int age;
};
int struct_cmp_age(const void* e1, const void* e2)
{
return (((struct stu*)e1)->age - ((struct stu*)e2)->age);//转变后的指针必须用括号括起来
}
int main()
{
struct stu s[3] = {
{
"zhangsan",18},{
"lisi",25},{
"wangwu",21} };
int sz = sizeof(s) / sizeof(s[0]);
int width = sizeof(s[0]);
my_qsort(s, sz, width, struct_cmp_age);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%s,%d\n", s[i].name, s[i].age);
}
return 0;
}
与库函数qsort的结果一样,也是将三个人按照年龄从小到大排列。
将比较函数修改为按照名字排序后
int struct_cmp_name(const void* e1, const void* e2)
{
return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
}
同样与库函数qsort的结果一样,按照名字首字母的ASCII码值从小到大排列。
以上俩种数据类型的排序中,my_qsort函数和Swap函数没有写出来,它们的具体内容参照写my_qsort函数过程即可。
在写my_qsort函数过程中,充分使用了回调函数,主要的应用就是在比较函数上,无论是我们自己模拟的my_qsort还是库函数qsort,在用户自定义好比较函数后,调用该函数都是使用的回调函数的方式。
C语言指针结语
C语言的特点主要在于指针的灵活使用,在通过《C语言指针(上篇、中篇、下篇、特别篇)》四篇文章的介绍,将指针由浅入深,由表即里详细的讲述了一遍,涉及到的各种指针类型以及它们的用法可以应付我们在编程过程中遇到的绝对大多数场景。
边栏推荐
- Lenovo hybrid cloud Lenovo xcloud: 4 major product lines +it service portal
- 模拟卷Leetcode【普通】1706. 球会落何处
- Greenplum6.x搭建_安装
- 为不同类型设备构建应用的三大更新 | 2022 I/O 重点回顾
- oracle一次性说清楚,多种分隔符的一个字段拆分多行,再多行多列多种分隔符拆多行,最终处理超亿亿。。亿级别数据量
- Three updates to build applications for different types of devices | 2022 i/o key review
- 【ChaosBlade:节点磁盘填充、杀节点上指定进程、挂起节点上指定进程】
- Interpretation of MySQL optimization principle
- Un salaire annuel de 50 W Ali P8 vous montrera comment passer du test
- 2022-06-30 unity core 8 - model import
猜你喜欢
H3C VXLAN配置
2022-07-06 unity core 9 - 3D animation
Lenovo hybrid cloud Lenovo xcloud: 4 major product lines +it service portal
C language for calculating the product of two matrices
Panel display technology: LCD and OLED
数字三角形模型 AcWing 1027. 方格取数
外部中断实现按键实验
ESP32-ULP协处理器低功耗模式RTC GPIO中断唤醒
Digital triangle model acwing 275 Pass a note
2022-06-30 Unity核心8——模型导入
随机推荐
ChaosBlade:混沌工程简介(一)
Simulation volume leetcode [general] 1557 The minimum number of points that can reach all points
Personal deduction topic classification record
Output all composite numbers between 6 and 1000
NCS Chengdu Xindian interview experience
2022-07-06 unity core 9 - 3D animation
OpenGL三维图形绘制
Tronapi wave field interface - source code without encryption - can be opened twice - interface document attached - package based on thinkphp5 - detailed guidance of the author - July 6, 2022 - Novice
【Istio Network CRD VirtualService、Envoyfilter】
Data analysis methodology and previous experience summary 2 [notes dry goods]
Simulation volume leetcode [general] 1706 Where does the ball meet
let const
Redis summary
Analysis of using jsonp cross domain vulnerability and XSS vulnerability in honeypot
Three updates to build applications for different types of devices | 2022 i/o key review
Greenplum6.x重新初始化
Simulation volume leetcode [general] 1567 Length of the longest subarray whose product is a positive number
How to add a mask of a target in a picture
A bug using module project in idea
Database storage - table partition