C语言指针进阶

✅作者简介:嵌入式入坑者,与大家一起加油,希望文章能够帮助各位!!!!
个人主页:@rivencode的个人主页
系列专栏:玩转C语言
推荐一款模拟面试、刷题神器,从基础到大厂面试题点击跳转刷题网站进行注册学习

目录

  • 一.回调函数
    • 1.qsort函数的介绍
    • 2.模拟实现qsort函数
  • 二.深度理解二维数组
    • 1.二维数组
    • 2.指针解引用与指针运算
  • 三.强制类型转化
  • 四.指针笔试题

一.回调函数

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

1.qsort函数的介绍

C语言指针进阶_第1张图片
C语言指针进阶_第2张图片

总结:

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

C语言指针进阶_第3张图片
要用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;
}


C语言指针进阶_第4张图片
C语言指针进阶_第5张图片
C语言指针进阶_第6张图片
交换两个元素:
C语言指针进阶_第7张图片

二.深度理解二维数组

1.二维数组

首先看一下二维数组元素在内存中如何存储
C语言指针进阶_第8张图片
以前我们的思维可能是二维数组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]是一个存储三个字符的一维数组。
C语言指针进阶_第9张图片
C语言指针进阶_第10张图片
C语言指针进阶_第11张图片

三维数组
C语言指针进阶_第12张图片
其实三维数组也可以看成是一维数组,char[2][4][3]数组有两个元素,只不过每一个元素的类型是 char[4][3]是一个二维数组,也就是说数组有两个元素每个元素是一个二维数组。

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

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

sizeof(arr)-数组名代表整个数组,计算的是整个数组的大小(字节)
C语言指针进阶_第13张图片

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

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

如何知道指针指向变量的类型呢
C语言指针进阶_第14张图片

C语言指针进阶_第15张图片

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

C语言指针进阶_第16张图片
C语言指针进阶_第17张图片

三.强制类型转化

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

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

C语言指针进阶_第18张图片
*如果等号两边的数据类型不一样,编译器就会告警,此时我们将&a的类型从int * 强制转化为char 目的就是为了编译器不告警,且代码具有字描述性。
C语言指针进阶_第19张图片
C语言指针进阶_第20张图片
C语言指针进阶_第21张图片

对指针强制类型转化,会影响指针的解引用与指针运算的问题
C语言指针进阶_第22张图片

四.指针笔试题

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

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

其余情况数组名代表的是首元素的地址
C语言指针进阶_第23张图片
C语言指针进阶_第24张图片

C语言指针进阶_第25张图片
C语言指针进阶_第26张图片

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

单独一串 “abcdef” 就代表a字符的地址
C语言指针进阶_第27张图片

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

C语言指针进阶_第28张图片
C语言指针进阶_第29张图片

二维数组

C语言指针进阶_第30张图片

  • 练习1:

C语言指针进阶_第31张图片
答案:
在这里插入图片描述
详细解析:
C语言指针进阶_第32张图片

  • 练习2:

C语言指针进阶_第33张图片
答案:

C语言指针进阶_第34张图片
详细解析:
C语言指针进阶_第35张图片
强制类型转化只会改变数据的类型不会改变其内容,改变看待数据的方式

  • 练习3:

C语言指针进阶_第36张图片
答案:
C语言指针进阶_第37张图片
详细解析:
C语言指针进阶_第38张图片

  • 练习4:
    在这里插入图片描述
    答案:
    C语言指针进阶_第39张图片

详细解析:
C语言指针进阶_第40张图片

  • 练习5:
    C语言指针进阶_第41张图片

答案:
C语言指针进阶_第42张图片

详细解析:
C语言指针进阶_第43张图片
C语言指针进阶_第44张图片

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

详细解析:
C语言指针进阶_第45张图片

  • 练习7:
    C语言指针进阶_第46张图片

答案:
C语言指针进阶_第47张图片
详细解析:

C语言指针进阶_第48张图片

  • 练习8:
    C语言指针进阶_第49张图片

答案:
C语言指针进阶_第50张图片

详细解析:

C语言指针进阶_第51张图片
C语言指针进阶_第52张图片
C语言指针进阶_第53张图片
C语言指针进阶_第54张图片
C语言指针进阶_第55张图片
这里需要注意的是:++会改变cpp的值,而像cpp+1 不会改变cpp的值。

你可能感兴趣的:(玩转C语言,c语言,c++,开发语言)