【c语言】指针进阶(超详细)

news2024/11/24 13:52:19

在这里插入图片描述

文章目录

  • ✈ 指向函数指针数组的指针
    • 📌指向函数指针数组的指针的定义
    • 📌指向函数指针数组的数组指针的使用
  • ✈回调函数
    • 📌 回调函数的定义
    • 📌 回调函数的使用
  • ✈qsort函数
    • 📌 qsort函数的作用
    • 📌qsort函数的定义
    • 📌qsort函数的头文件
    • 📌1.qsort函数排序整型数组
      • 比较函数compare
      • 主函数
    • 📌2.qsort函数排列结构体数据
      • 【1】排列结构体中的整型类型
        • 比较函数
        • 主函数以及结构体
      • 【2】排列结构体中的字符串类型
        • 比较函数compare
        • 主函数
      • 【3】 排列结构体中的浮点型数据
        • 比较函数compare
        • 主函数
    • 📌3.qsort函数排列字符数组类型数据
        • 比较函数compare
        • 主函数
  • ✈使用回调函数,模拟实现qsort(采用冒泡的方式)
    • 冒泡排序(传送门在此[冒泡排序](https://blog.csdn.net/yyqzjw/article/details/131909947?spm=1001.2014.3001.5501))
    • 模拟实现qsort排整型数组(利用冒泡排序方式)
  • 总结


✈ 指向函数指针数组的指针

指向函数指针数组的指针是一个 指针, 指针指向一个 数组 ,数组的元素都是 函数指针 ;
如何定义?

📌指向函数指针数组的指针的定义

我们可以先定义五个函数

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;
}

分别计算两个变量的加减乘除,然后定义一个函数指针数组用来存放上面4个函数的地址

int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; 

空出来一个是为了让加减乘除函数与下标对应上

int (*(*pp)[5])(int x,int y) = &p;

这个是指向函数指针数组的数组指针,ta指向的函数指针数组的类型是去掉*pp

int (*()[5])(int x,int y)

这个类型说明数组指针指向的是数组长度是5,每个元素都是地址,每个元素的类型是

int ()(int x,int y)

画图说明一下关系
在这里插入图片描述

📌指向函数指针数组的数组指针的使用

怎么通过这个数组指针pp去使用加减乘除这几个函数呢???代码如下

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(*p[5])(int x, int y) = { 0, add, sub, mul, div };
    int (*(*pp)[5])(int x, int y) = &p;
    int ret = (*(*pp + 1))(3, 4);
    printf("%d", ret);
}

在这里插入图片描述
在这块解释一下子

 (*(*pp + 1))(3, 4)

pp存放的是p[]整个数组的地址,*pp相当于拿到这个数组,也相当于拿到数组的数组名,相当于数组首元素地址,*pp+1为第二个元素地址, 如果在对其解引用得到的是 *(pp+1),这个就是数组第二个元素的内容,也就是说add的地址,因为&add和add打印的结果一样,使用该函数计算3+4;
add(3,4)等价 (
(*pp + 1))(3, 4)

✈回调函数

📌 回调函数的定义

回调函数pp()就是一个通过函数指针void(*p)()调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数print(&pp);,当这个指针被用来调用其所指向的函数时pp(),我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在if (1)特定的事件或条件发生时由另外的一方调用的,用于对该事件或
条件进行响应。

📌 回调函数的使用

void pp()
{
	printf("hahahahaha\n");
}
void print(void(*p)())
{
	if (1)
	{
		p();
	}
}
int main()
{  print(&pp);
	return 0;
}

根据回调函数的定义,可知pp()函数就是回调函数。

✈qsort函数

📌 qsort函数的作用

我们之前学过的冒泡排序,可以将一个整型数组排好序,如果让我们去排序浮点型,字符型,结构体型,我们应该怎么办呢
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
qsort函数可以解决这个问题,万物皆可排。那谁谁你怎么插队呢快去排队

📌qsort函数的定义

在这里插入图片描述
在msdn上找到定义,我们可以将定义复制过来

void qsort( void *base,
           size_t num,
           size_t width,
  int (__cdecl *compare )(const void *elem1, const void *elem2 ) );

在这里插入图片描述
什么???英格力士

在这里插入图片描述
下面有请过英语四级的小张同学给翻译翻译哈哈哈哈哈
咳咳,我来了

🚫1.base指针指向的是要排序数组的首地址,因为不清楚要排序的是什么类型的数据,这里用void 可以接收任何类型的指针
2.num是该数组的元素个数
3.width是每个元素的字节大小
4.自定义比较函数compare
第四个位置是一个函数指针来接收一个比较函数的地址,参数用两个指针接收要比较两个元素的地址,因为不知道比较什么类型的数据,就用void
的指针接收,比较函数是根据你自己要排列的数据类型自己定义的。在这里插入图片描述
第一个元素大于第二个元素返回大于0的,小于第二个元素返回小于0,等于返回0

📌qsort函数的头文件

在这里插入图片描述

📌1.qsort函数排序整型数组

比较函数compare

int int_cmp(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}

这里要排整型数据,将void的指针强制类型转化为int,一次可以访问4个字节 ,解引用也可以得到一个整型数据

主函数


int main()
{
	int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
	int i = 0;

	qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

在这里插入图片描述

注意:如果要排成降序只需要将比较函数中的
int int_cmp(const void* p1, const void* p2)
{
return ((int)p2 - (int)p1);
}

📌2.qsort函数排列结构体数据

【1】排列结构体中的整型类型

比较函数

int int_cmp_age(const void* p1, const void* p2)//按年龄比较
{
	return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}

主函数以及结构体

struct Stu {
	char name[20];
	int age;
	float score;
};
int main()
{

	int i = 0;
	struct Stu arr[3] = {{"zhangjiawang",18,100},{"zhumiao",50,76},{"liuliu",19,76}};
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr,sz, sizeof(arr[0]), int_cmp_age);
		for (int i = 0; i < 3; i++)
	{
		printf("%d ", arr[i].age);


	}

	return 0;
}

运行结果在这里插入图片描述

【2】排列结构体中的字符串类型

比较函数compare

int int_cmp_age(const void* p1, const void* p2)
{
	return (strcmp(((struct Stu*)p1)->name), ((struct Stu*)p2)->name);
}

字符串比较用strcmp函数,记得添加头文件string.h在这里插入图片描述
这个是strcmp函数的返回值,和比较函数刚好对应上

主函数

int main()
{

	int i = 0;
	struct Stu arr[3] = {{"zhangjiawang",18,100},{"zhumiao",50,76},{"liuliu",19,76}};
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr,sz, sizeof(arr[0]), int_cmp_age);
	for (int i = 0; i < 3; i++)
	{
		printf("%s ", arr[i].name);


	}
	return 0;
}

运行结果在这里插入图片描述

【3】 排列结构体中的浮点型数据

比较函数compare

int int_cmp_float(const void* p1, const void* p2)
{
	return (*(float*)p1 -*(float*)p2);
}

主函数

int main()
{

	int i = 0;
	struct Stu arr[3] = {{"zhangjiawang",18,100.0},{"zhumiao",50,76.0},{"liuliu",19,77.0}};
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr,sz, sizeof(arr[0]), int_cmp_float);
	for (int i = 0; i < 3; i++)
	{
		printf("%.1f ", arr[i].score);


	}
	return 0;
}

运行结果:在这里插入图片描述

📌3.qsort函数排列字符数组类型数据

比较函数compare

int int_cmp(const void* p1, const void* p2)
{
	return (*(char*)p1 - *(char*)p2);
}

主函数

int main()
{
	char arr[] = {'b','c','a','f','z','q'};
	int i = 0;

	qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(char), int_cmp);
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%c ", arr[i]);
	}
	printf("\n");
	return 0;
}

运行结果在这里插入图片描述

✈使用回调函数,模拟实现qsort(采用冒泡的方式)

冒泡排序(传送门在此冒泡排序)

int main()
{
	int  arr[10] = {10,9,8,7,6,5,4,3,2,1};
	int sz = sizeof(arr) / sizeof(arr[0]);//数组长度
	int i;
	for (i = 0; i < sz - 1; i++)//趟数
	{
		int j = 0;
		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;
			}
		}
    }
	for (i = 0; i < sz; i++)//打印数组
	{
		printf("%d ", arr[i]);
	}
}

模拟实现qsort排整型数组(利用冒泡排序方式)

主函数

int main()
{
	int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
	int i = 0;

	bubble_qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

模拟qsort函数

在这里插入图片描述
将冒泡排序的实现copy过来,然后对其修改
在这里插入图片描述
大概替换的思想是
`void bubble_sort(void* base, int num, int width, int cmp)
{int i;
for (i = 0; i < num - 1; i++)
{
int j = 0;
for (j = 0; j < num - 1 - i; j++)
{
if (比较函数的返回值)
{交换数组元素的函数}
}
}
}

比较函数compare(这里因为要排的是整型数组,用上面整型数组的比较函数就行)

int cmp(const void* p1, const void* p2)
{
return ((int)p1 - (int)p2);
}

现在的问题是怎么使用bubble_sort函数的参数找到要比较两个元素的地址??
在这里插入图片描述

代码:cmp((char*)base+j*width),(char*)base+(j+1)*width)>0
问题1:为什么要将base地址存在char的指针中?
方便访问任意类型数据,因为width是一个元素的字节大小,如果访问一个整型的地址,width就是4,char
的指针+4刚好跳过4个字节,如果把base地址用int*接收,如果width还是4的话,+4就跳过的不是一个元素,而是4个元素,相邻的元素根本就比不了大小。
如果j=0的话,上面传到比较函数中的就是第一个元素地址和第二个元素地址
替换后的bubble_sort

void bubble_sort(void* base, int num, int width, int cmp)
{
	int i;
	for (i = 0; i < num - 1; i++)
	{
		int j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base + j * width), (char*)base + (j + 1) * width) > 0)
			{
				交换数组元素的函数
			}
		}
	}

}
}
现在的问题是怎么交换两个地址上的元素。


交换数组元素的函数👇
void swap(char* p1, char* p2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{

		char tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		p1++;
		p2++;
	}
}

这里的交换函数是按字节交换的,比如说数组前两个元素1,3,假如说内存为小端存储,实现前两个元素交换在这里插入图片描述
替换后的bubble_sort

void bubble_sort(void* base, int num, int width, int cmp)
{
	int i;
	for (i = 0; i < num - 1; i++)
	{
		int j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base + j * width), (char*)base + (j + 1) * width)> 0)
			{
			swap((char*)base + j * width), (char*)base + (j + 1) * width));
			}
		}
	}

}
}

整体代码展示

`#include <stdio.h>
int cmp(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}
void swap(char* p1, char* p2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{

		char tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		p1++;
		p2++;
	}
}
void bubble_sort(void* base, int num, int width, int cmp(const void* p1, const void* p2))
{
	int i;
	for (i = 0; i < num - 1; i++)
	{
		int j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width)> 0)
			{
			swap((char*)base + j * width,(char*)base + (j + 1) * width,width);
			}
		}
	}

}
int main()
{
	int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
	int i = 0;

	qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), cmp);
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}`

总结

希望这篇文章可以给你带来帮助,如果有不对的地方,或者有哪里不理解的地方,请私信我,谢谢大家支持,下篇见,以上

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/861333.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OceanBase X Flink 基于原生分布式数据库构建实时计算解决方案

摘要&#xff1a;本文整理自 OceanBase 架构师周跃跃&#xff0c;在 Flink Forward Asia 2022 实时湖仓专场的分享。本篇内容主要分为四个部分&#xff1a; 分布式数据库 OceanBase 关键技术解读 生态对接以及典型应用场景 OceanBase X Flink 在游戏行业实践 未来展望 点击…

超导热催生meme,换汤不换药的投机轮回

文/章鱼哥 出品/陀螺财经 币圈对炒作meme概念的热情从未消亡过。 随着一种名为LK-99的物质被发现&#xff0c;围绕超导的兴奋不仅激发了科学界&#xff0c;加密货币相关概念也与之沸腾。不出所料&#xff0c;与此前围绕元宇宙、AI大肆炒作一样&#xff0c;许多meme代币已经出现…

多货币多汇率跨境电子商城建设(仓储管理、网络安全)

多货币多汇率跨境电子商城建设需要考虑到多个方面&#xff0c;包括仓储管理、网络安全、货币兑换、物流配送等。以下是具体的介绍&#xff1a; 一、仓储管理 仓储管理是跨境电子商城的重要组成部分&#xff0c;需要考虑到商品的存储、管理和分拣等环节。以下是需要注意的几个…

Pytest三种运行方式

Pytest 运行方式共有三种&#xff1a; 1、主函数模式 运行所有 pytest.main() 指定模块 pytest.main([-vs],,./testcase/test_day1.py) 只运行testcase 下的test_day1.py 文件 指定目录 pytest.main([-vs]),./testcase) 只运行testcase 目录下的文件 通过nodeid指定用例…

轻量级托管平台gogs

https://github.com/gogs/gogs/blob/main/README_ZH.md

iOS问题记录 - Xcode 15安装低版本iOS模拟器(持续更新)

文章目录 前言开发环境问题描述问题分析1. 定位问题2. 逆向分析2.1. IDA Free2.2. Hopper Disassembler Demo 3. 模拟器日志4. supportedArchs 解决方案最后 前言 最近新需求很多&#xff0c;项目改动很大&#xff0c;开发完成后想测一遍在低版本iOS系统上的兼容性&#xff0c…

windows使用/服务(13)戴尔电脑怎么设置通电自动开机

戴尔pc机器通电自启动 1、将主机显示器键盘鼠标连接好后&#xff0c;按主机电源键开机 2、在开机过程中按键盘"F12",进入如下界面&#xff0c;选择“BIOS SETUP” 3、选择“Power Management” 4、选择“AC Recovery”&#xff0c;点选“Power On”&#xff0c;点击“…

Codeforces Round 828 (Div. 3)E题题解

文章目录 [Divisible Numbers (easy version)](https://codeforces.com/contest/1744/problem/E1)问题建模问题分析代码 [ Divisible Numbers (hard version)](https://codeforces.com/contest/1744/problem/E2)问题建模问题分析1.根据简单版本分析所求2.方法1通过因数分解得到…

竞赛项目 深度学习图像风格迁移 - opencv python

文章目录 0 前言1 VGG网络2 风格迁移3 内容损失4 风格损失5 主代码实现6 迁移模型实现7 效果展示8 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习图像风格迁移 - opencv python 该项目较为新颖&#xff0c;适合作为竞赛课题…

MongoDB安装和配置

一、MongoDB安装和配置 1、进入官网下载你所需要的安装版本&#xff0c;点击直通官网 Step1&#xff1a;进入官网后&#xff0c;将看到如下界面&#xff0c;点击上方导航栏Products&#xff0c;找到Community Server Step2&#xff1a;选择自己需要的版本、系统和压缩方式 2、下…

Java算法_ 岛屿数量(LeetCode_Hot100)

题目描述&#xff1a;给你一个由 &#xff08;陆地&#xff09;和 &#xff08;水&#xff09;组成的的二维网格&#xff0c;请你计算网格中岛屿的数量。‘1’ , ‘0’ 岛屿总是被水包围&#xff0c;并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外&#…

Java:Stream API

文章目录 1 说明2 为什么要使用Stream API3 什么是StreamStream的操作三个步骤创建Stream实例一系列中间操作终止操作 1 说明 Java8中有两大最为重要的改变。第一个是 Lambda 表达式&#xff1b;另外一个则是 Stream API。Stream API ( java.util.stream) 把真正的函数式编程风…

Linux6.37 Kubernetes 集群调度

文章目录 计算机系统5G云计算第三章 LINUX Kubernetes 集群调度一、调度约束1.调度过程2.指定调度节点3.亲和性1&#xff09;节点亲和性2&#xff09;Pod 亲和性3&#xff09;键值运算关系 4.污点(Taint) 和 容忍(Tolerations)1&#xff09;污点(Taint)2&#xff09;容忍(Toler…

聊聊行锁、间隙锁、临键锁的区别

一、准备 创建 student 表 CREATE TABLE student ( id bigint NOT NULL, age int DEFAULT NULL, b int DEFAULT NULL, a int DEFAULT NULL, c int DEFAULT NULL, PRIMARY KEY ( id ), ) ENGINEInnoDB DEFAULT CHARSETutf8mb3 插⼊数据 insert into student (id,age,a,b,c) val…

Java【Spring】使用注解, 更简单的存储和获取 Bean

文章目录 前言一、存储 Bean1, 配置文件2, 五大类注解Bean 的命名规则 3, 方法注解Bean 的命名规则 二、获取 Bean1, 属性注入2, Setter 注入3, 构造方法注入4, Autowired 和 Resource 的区别5, 同一个类型的多个 Bean 注入问题 总结 前言 各位读者好, 我是小陈, 这是我的个人主…

机器学习、深度学习项目开发业务数据场景梳理汇总记录三

本文的主要作用是对历史项目开发过程中接触到的业务数据进行整体的汇总梳理&#xff0c;文章会随着项目的开发推进不断更新。 这里是续文&#xff0c;因为CSDN单篇文章内容太大的话就会崩溃的&#xff0c;别问我怎么知道的&#xff0c;问就是血泪教训&#xff0c;辛辛苦苦写了一…

C语言 指针与const

const 修饰变量&#xff0c;使得这个变量不能被修改。 const 对指针具有两种修饰的方式&#xff0c;且两种方式所限制的情况不同。 当const在 * 的左边 const int * p &n; 或者 int const * p &n; 当const在*的左边时&#xff0c;指针变量p所指向的空间内容无法被修…

C语言 指针与assert

assert 又称断言&#xff0c;需要包含头文件 assert.h 用于在运行时确保程序符合指定条件&#xff0c;如果不符合&#xff0c;就报错终止运行。 assert(p ! NULL); 上面代码在程序运行到这一行语句时&#xff0c;验证变量 p 是否等于 NULL。如果确实不等于 NULL &#xff0c;…

慎写指针类型的全局变量

简述: 在 关于range二三事[1] 第二个case中,介绍了对于指针类型的 切片/map变量A 的循环,要格外注意, 迭代出的value作用域是整个方法而非循环体内. 改进办法:在循环体中引入中间变量,"暂存"下每次迭代的value的值 但对于这个A,如果是全局变量,则又极有可能出现问题:…

Apache Maven简介安装及系统坏境配置eclipse配置Apache Maven---详细介绍

一&#xff0c;简介 Maven可以简化项目的构建和依赖管理&#xff0c;并提供了一种规范化和可复用的方式来管理Java项目。它广泛应用于Java开发领域&#xff0c;简单来说&#xff1a;它提供了一个简单而强大的方式来管理项目的构建、依赖关系和文档在企业级项目中被广泛采用。 1…