C语言指针进阶

✅作者简介:嵌入式入坑者,与大家一起加油,希望文章能够帮助各位!!!!
📃个人主页:@rivencode的个人主页
🔥系列专栏:玩转C语言
💬保持学习、保持热爱、认真分享、一起进步!!

一.回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
通俗点就是你将一个函数的地址当做形参传给另外一个函数,另外一个函数既然要接收一个函数的地址则它的函数参数就为一个函数指针,然后通过函数指针去调用原来的函数,则称这个函数为回调函数。

1.qsort函数的介绍

在这里插入图片描述
在这里插入图片描述

总结:

qsort函数参数:
void* base:数组中第一个元素的地址,用void * 指针类型的指针接收
size_t num:数组的元素个数
size_t size:数组每个元素的大小(单位为字节)
int (*compar)(const void * e1,const void * e2) :比较数组两个元素的函数的地址,用函数的指针接收。
该函数的参数为:
e1:要比较的第一个元素的地址,用void *指针来接收
e2:要比较的第一个元素的地址,用void *指针来接收
该函数的返回值:为整形
当e1元素大于e2元素 返回1
当e1元素等于e2元素 返回0
当e1元素等于e2元素 返回-1

在这里插入图片描述
要用void *来接收元素的地址,因为要满足不同数据类型的元素比较即排序

2.模拟实现qsort函数

typedef struct stu
{
	char name[30];
	int age;
}stu;

void Swap(char* buffer1, char *buffer2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buffer1;
		*buffer1 = *buffer2;
		*buffer2 = tmp;
		buffer1++;
		buffer2++;
	}
}

int Cmp_Int(void *e1, void *e2)
{
	return *(int *)e1 - *(int *)e2;
}

int  Cmp_Float(void *e1, void *e2)
{
	if (*(float*)e1 - *(float*)e2 > 0)
		return 1;
	else
		return -1;
}

int  Cmp_Stu_age(void *e1, void *e2)
{
	return (((stu *)e1)->age - ((stu *)e2)->age);
}

int  Cmp_Stu_name(void *e1, void *e2)
{
	return strcmp(((stu*)e1)->name, ((stu*)e2)->name);
}

void Bubble_Sort(void*base, int num, int width, int(*cmp)(void *e1, void *e2))
{
	int i = 0;
	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);
			}
		}
	}
}



void Test_int()
{
	int i = 0;
	int arr[10] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
	int num = sizeof(arr) / sizeof(arr[0]);
	Bubble_Sort(arr, num, sizeof(arr[0]), Cmp_Int);
	for (i = 0; i < num; i++)
	{
		printf("%d ",arr[i]);
	}
}
void Test_float()
{
	int i = 0;
	float arr[10] = { 3.14, 2.5, 3.6, 7.8, 9.8, 6.6, 5.5, 4.4, 3.3, 2.2 };
	int num = sizeof(arr) / sizeof(arr[0]);
	Bubble_Sort(arr, num, sizeof(arr[0]), Cmp_Float);
	for (i = 0; i < num; i++)
	{
		printf("%lf ", arr[i]);
	}
}

void Test_stu_age()
{
	stu s[3] = { { "c", 30 }, { "b", 20 }, {"a",10} };
	int num = sizeof(s) / sizeof(s[0]);
	Bubble_Sort(s, num, sizeof(s[0]), Cmp_Stu_age);
}
void Test_stu_name()
{
	stu s[3] = { { "c", 30 }, { "b", 20 }, { "a", 10 } };
	int num = sizeof(s) / sizeof(s[0]);
	Bubble_Sort(s, num, sizeof(s[0]), Cmp_Stu_name);
}


int main()
{
	//Test_int();
	//Test_float();
	//Test_struct();
	//Test_stu_name();
	return 0;
}


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
交换两个元素:
在这里插入图片描述

二.深度理解二维数组

1.二维数组

首先看一下二维数组元素在内存中如何存储
在这里插入图片描述
以前我们的思维可能是二维数组char a[3][4]是一个三行四列这样的布局,其实在内存中真正的是所有元素都是连续存储但为什么会出现这样的结果呢。

先说结论:

不管是二维还是三维还是多维数组都可以看做是’一维数组’****,就拿二维数组来说char a[3][4],数组名与 [3] 结合说明数组有三个元素,而数组存储元素的类型是char [4],我们都知道char [4]是一个一维数组的类型,总结:char a[3][4] 是一个有3个元素的数组,而每个元素是一个一维数组。

数组类型 :去掉数组名剩余的部分

int arr[10]; ->类型为int[10]表示数组存储10个整形元素
char arr[10];->类型为char[10]表示数组存储10个字符元素
int*arr[10];->类型为int*[10]表示数组存储10个整形指针
int (*parr[10])(int ,int );->类型为int(*[5])(int ,int )
表示数组存储10个函数指针

数组中元素的类型:去掉数组名与[元素个数] 剩余的部分

int arr[10]; ->数组中元素的类型为int,数组存储10个整形
char arr[10];->数组中元素的类型为char,数组存储10个字符型
int*arr[10];->数组中元素的类型为 int *,数组存储10个整形指针
int (*parr[10])(int ,int );->数组中元素的类型为int (*)(int ,int ),数组存储10个函数指针

char a[3][4];-> 去掉 a[3],剩下的 char [4]的数组中存储的元素类型,而char [4]是一个存储4个字符的一维数组,则char a[3][4]有3个元素,每个元素是一个一维数组。

则就能解释为什么二维数组的元素存储都是连续,因为char a[3][4] 是一个数组,数组三个元素是连续存储,而每个元素是一个一维数组又是连续存储的,所以整个二维数组的元素都是连续存储的。

这里我画了个 char c[4][3] ->数组有4个元素,每个元素的类型是 char [3]是一个存储三个字符的一维数组。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三维数组
在这里插入图片描述
其实三维数组也可以看成是一维数组,char[2][4][3]数组有两个元素,只不过每一个元素的类型是 char[4][3]是一个二维数组,也就是说数组有两个元素每个元素是一个二维数组。

四维 ,五维…数组以此类推。

二维数组也是数组所以满足:
&arr-数组名代表整个数组,取出的是整个数组的地址

sizeof(arr)-数组名代表整个数组,计算的是整个数组的大小(字节)
在这里插入图片描述

2.指针解引用与指针运算

  • 指针解引用:取出sizeof(指针指向变量类型)个字节的内容

如何知道指针指向变量的类型呢
在这里插入图片描述

在这里插入图片描述

  • 指针加1其实是加上sizeof(其指针指向变量的类型)

在这里插入图片描述
在这里插入图片描述

三.强制类型转化

先说结论:
1.让编译器不报警告
2.让程序员看增加代码可读性
3.对指针强制类型转化,会影响指针的解引用与指针运算的问题
4.强制类型转化后,后面所有运算问题按照强转后的新类型来计算

强制类型转化是改变的是对特定内容的看待方式,只会改变数据的类型,并不会改变数据在内存中存储的二进制序列。

在这里插入图片描述
*如果等号两边的数据类型不一样,编译器就会告警,此时我们将&a的类型从int * 强制转化为char 目的就是为了编译器不告警,且代码具有字描述性。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

对指针强制类型转化,会影响指针的解引用与指针运算的问题
在这里插入图片描述

四.指针笔试题

&arr-数组名代表整个数组,取出的是整个数组的地址

sizeof(arr)-数组名代表整个数组,计算的是整个数组的大小(字节)

其余情况数组名代表的是首元素的地址
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

const char * str=“abcdef” 与 char buffer[]="abcdef"的区别
先说结论:
字符串常量是存放在字符常量区与全局变量一样生命周期是从程序运行到结束,而且字符串常量是不可被修改。

单独一串 “abcdef” 就代表a字符的地址
在这里插入图片描述

char buffer[]=“abcdef” 字符串被保存在数组中, 而数组又是在栈上开辟(局部数组),函数运行完时数组被销毁字符串也被销毁,保存在数组中的字符串可以被修改。

在这里插入图片描述
在这里插入图片描述

二维数组

在这里插入图片描述

  • 练习1:

在这里插入图片描述
答案:
在这里插入图片描述
详细解析:
在这里插入图片描述

  • 练习2:

在这里插入图片描述
答案:

在这里插入图片描述
详细解析:
在这里插入图片描述
强制类型转化只会改变数据的类型不会改变其内容,改变看待数据的方式

  • 练习3:

在这里插入图片描述
答案:
在这里插入图片描述
详细解析:
在这里插入图片描述

  • 练习4:
    在这里插入图片描述
    答案:
    在这里插入图片描述

详细解析:
在这里插入图片描述

  • 练习5:
    在这里插入图片描述

答案:
在这里插入图片描述

详细解析:
在这里插入图片描述
在这里插入图片描述

  • 练习6:
    在这里插入图片描述
    答案:
    在这里插入图片描述

详细解析:
在这里插入图片描述

  • 练习7:
    在这里插入图片描述

答案:
在这里插入图片描述
详细解析:

在这里插入图片描述

  • 练习8:
    在这里插入图片描述

答案:
在这里插入图片描述

详细解析:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里需要注意的是:++会改变cpp的值,而像cpp+1 不会改变cpp的值。

  • 39
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 44
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 44
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

rivencode

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值