带你学C语言-指针(4)

这篇具有很好参考价值的文章主要介绍了带你学C语言-指针(4)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

带你学C语言-指针(4),c语言,开发语言,c++,算法,数据结构

带你学C语言-指针(4),c语言,开发语言,c++,算法,数据结构

带你学C语言-指针(4),c语言,开发语言,c++,算法,数据结构

目录

​编辑

⚾0.前言

🏀1.回调函数

⚽2.qsort

🏉2.1 qsort函数的模拟实现

🎾3.sizeof与strlen对比

🎾4.结束语


带你学C语言-指针(4),c语言,开发语言,c++,算法,数据结构

⚾0.前言

        言C之言,聊C之识,以C会友,共向远方。各位CSDN的各位你们好啊,这里是持续分享C语言知识的小赵同学,今天要分享的C语言知识是深入了解指针(4),在这一章,小赵将会和大家继续聊指针的相关内容。✊

🏀1.回调函数

那么首先我们要了解的就是什么是回调函数呢?其实回调函数指的就是我们在使用一个函数的时候,我们讲另一个函数以地址的形式传入这个函数中,然后我们在这个函数中,通过函数指针去调用我们传入的函数,而这个我们传入的函数其实也就是我们回调函数。那么废话不多说它的出现究竟可以给我们的代码带来哪些便捷呢?下面我们看下面这样一个代码。

#include <stdio.h>
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 = %d\n", ret);
			break;
		case 2:
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = sub(x, y);
			printf("ret = %d\n", ret);
			break;
		case 3:
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = mul(x, y);
			printf("ret = %d\n", ret);
			break;
		case 4:
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = div(x, y);
			printf("ret = %d\n", ret);
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}

这一个代码 其实也就是我们上一章节时候使用的计算器的代码,在上一章其实我们已经用过我们的函数指针数组对这个代码进行过一次优化,那么这个代码能否用我们的回调函数进行优化呢?那么该如何优化呢其实也就是对那他原本的输入数字的方式稍微改一下。

在这里我们先创建一个函数

void calc(int(*pf)(int, int))
{
	int ret = 0;
	int x, y;
	printf("输⼊操作数:");
	scanf("%d %d", &x, &y);
	ret = pf(x, y);
	printf("ret = %d\n", ret);
}

带你学C语言-指针(4),c语言,开发语言,c++,算法,数据结构

这个其实就相当于二次集装箱,把我们的函数再次进行一次打包,那么我们下面的主函数调用时候只需要讲函数名导入函数中就可以了。就不用分开在一次一次敲输入的代码了。 

int main()
{
	int input = 1;
	do
	{
		printf("*************************\n");
		printf(" 1:add             2:sub \n");
		printf(" 3:mul             4:div \n");
		printf(" 0:exit                  \n");
		printf("*************************\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			calc(add);
			break;
		case 2:
			calc(sub);
			break;
		case 3:
			calc(mul);
			break;
		case 4:
			calc(div);
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}

带你学C语言-指针(4),c语言,开发语言,c++,算法,数据结构

⚽2.qsort

在这里请允许小赵为大家介绍一个函数叫qsort函数,这个函数有什么特点,一是实现快速排序,二是它里面使用了我们的回调函数,可以拿来作为我们的训练运用。

带你学C语言-指针(4),c语言,开发语言,c++,算法,数据结构

可能有人看到这些英文会一阵头疼,但是没关系,如果我们可以看懂它的列子(即知道它的作用)和它的参数就可以完美使用这个函数了。或者我们也可以试着去翻译这个英文,翻译器之类的使用上,实在不行我们就当时恶补英语好了哈哈。好了我们言归正传吗,我们可以看到这个函数它的参数,第一个是我们要排列的数组,第二个是数组的元素数量,第三个则是我们数组中每个元素的大小,最后一个则是比较函数。(这里的排列一定是针对我们元素是一样的,毕竟我们的数组里面的元素就是一样的,不对吗?),这个函数使用上唯一的烦就是我们要自己搞个比较函数,可以说是伤了很多人的脑袋,但实际上也是比较简单的。

带你学C语言-指针(4),c语言,开发语言,c++,算法,数据结构

(这个是对我们的比较函数的要求即两个数字相减如果为负数则小的在前,大的在后) 

这里举一个整数比较的例子(这里唯一要注意的是这个比较函数的参数类型是const void*)(这里为什么是const void*,因为const void*可以接受任意类型的值)。

#include <stdio.h>
int int_cmp(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}

当然我们有的时候要比较的不仅仅是数字还有字母等。

他们要怎么比较呢?这里我们就不得不用我们的字符串函数中的strcmp。

带你学C语言-指针(4),c语言,开发语言,c++,算法,数据结构

带你学C语言-指针(4),c语言,开发语言,c++,算法,数据结构 它的整个比较也是和我们上面的输出其实也是一样的。

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

那么我们在要用哪个的时候实现哪个就行,下面我们来演示两组。

#include <stdio.h>
#include<string.h>
//qosrt函数的使⽤者得实现⼀个⽐较函数
int int_cmp(const void* p1, const void* p2)
{
	return strcmp((char*)p1, (char*)p2);
}
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;
}
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;
}

🏉2.1 qsort函数的模拟实现

那么这样的一个函数究竟是如何实现的呢?下面我们就来试着去模拟一下这样一个函数的实现。

在排序的底层逻辑上我们可以使用我们的冒泡排序去实现我们的我们的排序,由于我们这一次要排的数据不再是单一的整数型,所以我们的数据交换,还要再弄一个函数,或者在原本函数进行改动(但是在原本函数上改动代码会有点偏长,小赵在这里就再弄一个函数去搞定这件事)。

交换函数

void _swap(void* p1, void* p2, int size)
{
	int i = 0;
	for (i = 0; i < size; i++)//这里其实就是对我们数据的每一个字节进行交换
	{
		char tmp = *((char*)p1 + i);//char刚好占一个一个字节
		*((char*)p1 + i) = *((char*)p2 + i);
		*((char*)p2 + i) = tmp;
	}
}

冒泡排序

void bubble(void* base, int count, int size, int(*cmp)(void*, void*))//这里主要就是冒泡排序前面的知识。
{
	int i = 0;
	int j = 0;
	for (i = 0; i < count - 1; i++)
	{
		for (j = 0; j < count - i - 1; j++)
		{
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)//这里是用了我们的比较函数,如果前面比后面的大
			{
				_swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}

 最后浓缩在一起

#include<stdio.h>
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 < size; i++)
	{
		char tmp = *((char*)p1 + i);
		*((char*)p1 + i) = *((char*)p2 + i);
		*((char*)p2 + i) = tmp;
	}
}
void bubble(void* base, int count, int size, int(*cmp)(void*, void*))//排序
{
	int i = 0;
	int j = 0;
	for (i = 0; i < count - 1; i++)
	{
		for (j = 0; j < count - i - 1; j++)
		{
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 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 < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);//打印我们的数组
	}
	printf("\n");
	return 0;
}

这样一个函数就可以对我们任意数据进行比较。 

带你学C语言-指针(4),c语言,开发语言,c++,算法,数据结构

🎾3.sizeof与strlen对比

sizeof和strlen函数其实也是我前面一直在和大家重点聊的两个家伙,这两个家伙我们在一起聊的时间其实很多,给小赵的感悟也很多,那么我们下面就再来看看这两个家伙究竟是什么不同,其实说起来也简单。

首先是sizeof这个操作符,这个函数的作用就是就算你给的东西所占的内存大小。而strlen则是统计我们字符串的长度或者字符串数组的长度,到‘\0’为止。可以说这个家伙真正打起来其实是在数组,如果不是在数组中这两个家伙几乎也打不起来。

好了下面我们结合我们的代码来聊

#include <stdio.h>
int main()
{
 char arr1[3] = {'a', 'b', 'c'};
 char arr2[] = "abc";
 printf("%d\n", strlen(arr1));
 printf("%d\n", strlen(arr2));
 
 printf("%d\n", sizeof(arr1));
 printf("%d\n", sizeof(arr1));
 return 0;
}

带你学C语言-指针(4),c语言,开发语言,c++,算法,数据结构

(这里要提一下我们的sizeof 可不会管你什么\0不\0只要你存在,那你今天就必须被记录下来。)

我们看到在字符串的统计中两个人都是很正常的,唯有在数组中我们的strlen似乎出了问题,那究竟 是什么问题呢?其实也很简单就是我们之前说的,strlen这个函数比较轴它一定要找到‘\0’为止,那么我们的字符串其实是会自动补一个‘\0’在字符串后面的,那我们的数组呢?我们的数组什么都不会补,那么我们死轴死轴的strlen就会一直去找一直去找我们的‘\0’终于它在一片茫茫的内存的黑暗中找到了我们的'\0',并且返回了它的位置,其实strlen函数这样也有点轴的可爱,不是吗?)那这个问题怎么解决呢?其实就是我们只需要在我们的第一个数组后面补一个‘\0’就可以了

带你学C语言-指针(4),c语言,开发语言,c++,算法,数据结构

🎾4.结束语

好了小赵今天的分享就到这里了,如果大家有什么不明白的地方可以在小赵的下方留言哦,同时如果小赵有什么地方说得不对也希望得到大家的指点,谢谢各位家人们的支持。你们的支持是小赵创作的动力,加油。

带你学C语言-指针(4),c语言,开发语言,c++,算法,数据结构

如果觉得文章对你有帮助的话,还请点赞,关注,收藏支持小赵,如有不足还请指点,小赵及时改正,感谢大家支持!!!带你学C语言-指针(4),c语言,开发语言,c++,算法,数据结构文章来源地址https://www.toymoban.com/news/detail-807844.html

到了这里,关于带你学C语言-指针(4)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • GRBL源码详解步进电机的算法学习笔记(STM32)一篇带你学完GRBL算法初版

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 GRBL源码中步进电机的算法学习笔记(STM32) GRBL源码中算法部分的学习是我在公司研发激光切割机(三轴)期间研究的一套关于步进电机驱动控制的常见算法,以下内容都以激光切割机来举例,话不多说

    2024年02月04日
    浏览(54)
  • 【c语言指针详解】复杂数据结构的指针用法

    目录 一、动态内存分配 1.1 使用malloc和free函数进行内存的动态分配和释放 1.2 内存泄漏和野指针的概念和解决方法 二、复杂数据结构的指针用法 2.1 结构体指针和成员访问操作符 2.2 指针数组和指向指针的指针 2.2.1 指针数组 2.2.2 指向指针的指针 2.3 动态内存分配与结构体

    2024年02月04日
    浏览(50)
  • 【数据结构与算法】之多指针算法经典问题

    本文为 【数据结构与算法】多指针算法经典问题 相关介绍,下边将对 链表反转 (包含 迭代反转链表 、 递归反转 、 头插法反转 ), 双指针-快慢指针 (包含 寻找单向无环链表的中点 、 判断单向链表是否有环及找环入口 ), 双指针-左右指针 (包含 两数之和 、 二分查

    2024年02月03日
    浏览(66)
  • [元带你学: eMMC协议详解 14] 数据擦除(Erase) 详解

    依JEDEC eMMC 5.1及经验辛苦整理,付费内容,禁止转载。 所在专栏 《元带你学:eMMC协议》 内容摘要 全文 4200字, 主要内容介绍了各种擦除操作概念以记用法,总结了不同擦除操作的区别, 根据不同安全级别和应用场景, 可以使用最合适的擦除类型。 前言 1 Erase 2 Trim 3 Sanit

    2024年02月09日
    浏览(48)
  • C语言自定义数据类型(三)结构体指针

    所谓结构体指针就是指向结构体变量的指针,一个结构体变量的起始地址就是这个结构体变量的指针。如果把一个结构体变量的起始地址存放在一个指针变量中,那么,这个指针变量就指向该结构体变量。 目录 一、指向结构体变量的指针 1.1举例说明 二、指向结构体数组的指

    2024年02月06日
    浏览(51)
  • 【数据结构】—堆详解(手把手带你用C语言实现)

                                            食用指南:本文在有C基础的情况下食用更佳                                          🔥 这就不得不推荐此专栏了: C语言                                        ♈️ 今日夜电波: 水星—今泉愛夏              

    2024年02月07日
    浏览(62)
  • 数据结构与算法----复习Part 8 (链表双指针)

    本系列是算法通关手册LeeCode的学习笔记 算法通关手册(LeetCode) | 算法通关手册(LeetCode) (itcharge.cn) 本系列为自用笔记,如有版权问题,请私聊我删除。 目录 一,双指针简介(Two Pointers) 二,起点不一致的快慢指针 三,步长不一致的快慢指针 判断链表中是否含有环: 四

    2024年02月19日
    浏览(60)
  • 手把手教你 ,带你彻底掌握八大排序算法【数据结构】

    直接插入排序是一种简单的插入排序法,其基本思想:是把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 可以理解为一遍摸扑克牌,一边进行排序 在待排序的元素中,假设前面n-1(其中n=2)个数

    2024年02月02日
    浏览(67)
  • 【数据结构与算法】三个经典案例带你了解动态规划

    从表中我们可以看到,最大的公共子串长度为2,一共有两个长度为2的公共子串,分别是第一个字符串的第2个字符到第3个字符和第一个字符串的第3个字符到第4个字符,即 ba 和 ac 根据上面的方法,我们来用代码封装一下求取最大公共子串的函数 function publicStr(s1, s2) { // 创建

    2024年04月09日
    浏览(95)
  • 手撕数据结构与算法——树(三指针描述一棵树)

    📖作者介绍:22级树莓人(计算机专业),热爱编程<目前在c++阶段, 因为最近参加新星计划算法赛道(白佬),所以加快了脚步,果然急迫感会增加动力 ——目标Windows,MySQL,Qt,数据结构与算法,Linux,多线程,会持续分享学习成果和小项目的 📖作者主页:king南星 📖

    2024年01月17日
    浏览(58)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包