都知道指针很重要,但你真的了解指针么?

目录

前言:

1.指针的基础概念

2.字符指针

3.指针数组

概念:

温馨提示:

4.数组指针

4.1数组指针的定义

概念:

 4.2 &数组名 VS 数组名

概念:

 4.3数组指针的使用

小总结:

5.数组参数、指针参数

5.1一维数组传参

5.2二维数组传参 

6.函数指针

概念:

小提升:

7.函数指针数组

概念:

 如何应用:

8.回调函数

概念:

qsort函数:

9.指针和数组笔试题 + 讲解

第一组:

第二组:

第三组:

第四组:

第五组:

第六组:

第七组:

第八组:

总结:

10.指针笔试题

笔试题一:

笔试题三:

笔试题四:

笔试题五:

笔试题六:

​笔试题七:

11.小结


前言:

        指针在C的的重要性不言而喻,指针用的好和指针不会用,相信大家都有一个自己的认识,接下来我就通过讲解 + 例题的形式为大家进行深度解剖指针,让大家对指针认识清楚,并应用自如~!

1.指针的基础概念

  1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
  2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。
  3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。

2.字符指针

        在指针的类型中我们知道有一种指针类型为字符指针 char*。

🏠一般使用:

int main()
{
    char ch = 'w';
    char *pc = &ch;
    *pc = 'w';
    return 0;
}

🏠还有一种使用方法:

int main()
{
    const char* pstr = "Liyuyuea~!";//这里是把一个字符串放到pstr指针变量里了吗?
    printf("%s\n", pstr);
    return 0;
}

        这里是把一个字符串放到pstr指针变量里了么?

        很显然不是的,指针大小不是4个字节么(默认X86)指针怎么可能存这么多字符呢,所以肯定不是将字符串存到指针变量里了,根据对指针的认识,得知指针存的是一个地址,那么只要存字符串的首元素地址就可以了

        通过字符串首元素地址就可以打印整个字符串~!

         上面代码的意思是把一个常量字符串的首字母 L 的地址放在了指针变量 pstr 里。

🚀那么接下来给大家看一个面试题~!

        大家先看看这个题会输出什么呢。

         我先给大家公布答案,看看你们做对了没~!

         想必有大佬做对了,那么这又是为什么呢?我给大家话个图就明白了~!

        


 3.指针数组

概念:

        指针数组就是数组,数组里的元素是指针。也就是一个存放指针的数组~!

int* arr1[10];         //整形指针的数组

char *arr2[4];        //一级字符指针的数组

char **arr3[5];      //二级字符指针的数组

温馨提示:

        我们一定要记住指针的概念,因为这里会有很多“套娃”,只有记住了哪个代码是什么,才不会搞混乱~!(接下来直接看数组指针,和数组指针一起进行区分!)


4.数组指针

4.1数组指针的定义

概念

        数组指针是一个指针,这个指针指向一个数组。也就是一个指向数组的指针~!

🍑根据 指针数组数组指针 的概念,我们来区分一下:

🍑想好了接下来看图~!

 4.2 &数组名 VS 数组名

概念:

  1. 数组名一般情况下都是首元素地址~!
  2. &数组名sizeof(数组名)的时候是数组地址~!

🏠那么我们看一段代码:

🚀运行结果如下:

       

        由此可见,他们的地址是相同的,可是我们刚才说 &数组名 是整个数组的地址阿,难道表示整个数组的表示地址和首元素地址没有区别么?是一样的么?

🏠接下来我们再看一段代码:

 🚀下面是运行的代码:

 4.3数组指针的使用

        我们懂了数组指针,那么数组指针的如何使用的呢?

        既然数组指针指向的是数组,那么数组指针里就应该存放数组,只要把数组放进去就好了~!

🚀代码如下:

🍑下面进行讲解~!

       

          相信经过讲解,大概该怎么去用大家已经有了一定的理解,接下来就是大家在实战中可以去尝试写更多代码去体会~!

小总结:

        学了指针数组和数组指针,我们一起来回顾并看看下面代码的意思:

🚀接下来看解析~


5.数组参数、指针参数

 在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?

5.1一维数组传参

🍑接下来我们判断一下这样传参可不可以~!

5.2二维数组传参 

🍑接下来我们判断一下这样传参可不可以~!

 


6.函数指针

概念

        函数指针是一个指针,这个指针指向一个函数。也就是指向函数的指针~!

🍑首先我们先来观察一段代码:

         输出的是两个地址,这两个地址是test函数的地址。

        那么我们的函数地址要想保存起来,是用什么形式进行保存呢?

 

🍑接下来就看解析~!

小提升:

         接下来我们来阅读两端有趣的代码~

//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);

        相信大家看到这两个代码肯定很苦恼吧(这是什么乱七八糟的!一堆括号堆在一起,我才不想看!),但你要掌握了原理,也是可以分析出来的!!!

         接下来第二个代码我就不进行解释了,你们自己可以去想想,如果有感兴趣的小伙伴可以在评论区留言,我会给你单独解释~!


7.函数指针数组

概念

        函数指针数组是一个数组,这个数组的元素是函数指针。所以函数指针数组是存放函数指针的数组~!

🍑我们首先还是先来判别哪个是函数指针数组:

int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];

 

如何应用:

        首先我们先看这样一个代码,这个代码是一个计算器。

#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("*************************\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;
}

        我们可以看出switch语句中有很多重复的语句,整体就很冗余,那么我们便可以利用函数指针数组的形式进行制作一个转移表

        如下列所示:

#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;
	int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
	while (input)
	{
		printf("*************************\n");
		printf(" 1:add           2:sub \n");
		printf(" 3:mul           4:div \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		if ((input <= 4 && input >= 1))
		{
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);
		}
		else
			printf("输入有误\n");
		printf("ret = %d\n", ret);
	}
	return 0;
}

        这样看起来是不是就舒服多啦~!所以我们要是利用好指针,不仅可以提供我们不同的思路,也可以让我们的代码看起来更漂亮~!


8.回调函数

概念:

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

qsort函数:

        所谓的qsort就是快速排列。

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

🚀它运行的结果就是把arr中数组的元素进行了排列,如图所示。

         接下来我会模拟实现qsort函数,但其实大家只要看哪里用到了回调函数就好,没必要会模拟实现,只要懂了就好~!

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

        只要清楚这几点就可以了~!

9.指针和数组笔试题 + 讲解

第一组:

 🍑大家先想想吧~!

第二组:

 🍑大家先想想吧~!

第三组:

 🍑大家先想想吧~!

第四组:

 🍑大家先想想吧~!

第五组:

 🍑大家先想想吧~!

第六组:

 🍑大家先想想吧~!

第七组:

 🍑大家先想想吧~!

第八组:

 🍑大家先想想吧~!

总结:

数组名的意义:

  1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
  2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
  3. 除此之外所有的数组名都表示首元素的地址。

10.指针笔试题

        如果你能看到这里,相信指针对你来说已经了如指掌了,但是还是缺少实战,接下来我会列出几个笔试题,支持反复观看~!

        大家做题前好好想想,动手画画,可能这里的笔试题会是你将来面试题的原题或者经过改编的哦,大家再认真看完吧~!!!

笔试题一:

 🍑大家先想想吧~!

笔试题二:

🍑大家先想想吧~!

笔试题三:

🍑大家先想想吧~!

笔试题四:

🍑大家先想想吧~!

 

笔试题五:

🍑大家先想想吧~!

 

笔试题六:

 

🍑大家先想想吧~!

笔试题七:

 

🍑大家先想想吧~!

11.小结

        本次内容纯干货,博主准备了好几天才整理好的,绝对够干~!

        可能看的时候会挺枯燥的,但是这点内容必须要掌握,而且要掌握好,只有掌握好这点才能正好的向下顺利学习~!

        其实最后还有一个笔试题我没写出来,那个有点复杂,我准备再出一篇博客,所以如果这点知识对你有帮助,麻烦你给博主个三连,博主才更有动力去总结并出讲解~!

        也不知道大家爱看干货还是喜欢实在的东西,大家如果有要求可以给我评论,都会看,都会看~!!! 

  • 20
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 20
    评论
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

NPC Online

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

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

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

打赏作者

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

抵扣说明:

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

余额充值