前言:
**指针在C的的重要性不言而喻,指针用的好和指针不会用,相信大家都有一个自己的认识,接下来我就通过讲解 + 例题的形式为大家进行深度解剖指针,让大家对指针认识清楚,并应用自如~!**![](https://img-blog.csdnimg.cn/8ca25ecc8d7c440fa5f569a7d4534675.jpeg)
1.指针的基础概念
- 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
- 指针的大小是固定的4/8个字节(32位平台/64位平台)。
- 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。
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 里。**
🚀那么接下来给大家看一个面试题~!
** 大家先看看这个题会输出什么呢。**
** 我先给大家公布答案,看看你们做对了没~!**![](https://img-blog.csdnimg.cn/c10b9d71618f461ea7a3c58379ae4a9f.gif)
** 想必有大佬做对了,那么这又是为什么呢?我给大家话个图就明白了~!**
![](https://img-blog.csdnimg.cn/4305c5c97ddc4a389c2b2b2de046589f.png)
3.指针数组
概念:
** 指针数组就是数组,数组里的元素是指针。也就是一个存放指针的数组~!**
int arr1[10]; //整形指针的数组*
*char arr2[4]; //一级字符指针的数组
**char arr3[5]; //二级字符指针的数组
温馨提示:
**我们一定要记住指针的概念,因为这里会有很多“套娃”,只有记住了哪个代码是什么,才不会搞混乱~!**(接下来直接看数组指针,和数组指针一起进行区分!)
4.数组指针
4.1数组指针的定义
概念:
** 数组指针是一个指针,这个指针指向一个数组。也就是一个指向数组的指针~!**
🍑根据 指针数组 和 数组指针 的概念,我们来区分一下:
🍑想好了接下来看图~!
4.2 &数组名 VS 数组名
概念:
- 数组名一般情况下都是首元素地址~!
- 在&数组名 和 sizeof(数组名)的时候是数组地址~!
🏠那么我们看一段代码:
🚀运行结果如下:
** 由此可见,他们的地址是相同的,可是我们刚才说 &数组名 是整个数组的地址阿,难道表示整个数组的表示地址和首元素地址没有区别么?是一样的么?**
🏠接下来我们再看一段代码:
🚀下面是运行的代码:
4.3数组指针的使用
** 我们懂了数组指针,那么数组指针的如何使用的呢?**![](https://img-blog.csdnimg.cn/26c7fa22294743ab87daae0f2abcc2b3.jpeg)
** 既然数组指针指向的是数组,那么数组指针里就应该存放数组,只要把数组放进去就好了~!**
🚀代码如下:
🍑下面进行讲解~!
** **
** 相信经过讲解,大概该怎么去用大家已经有了一定的理解,接下来就是大家在实战中可以去尝试写更多代码去体会~!**
小总结:
**学了指针数组和数组指针,我们一起来回顾并看看下面代码的意思:**
🚀接下来看解析~
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.指针和数组笔试题 + 讲解
第一组:
** 🍑大家先想想吧~!**
第二组:
** 🍑大家先想想吧~!**
第三组:
** 🍑大家先想想吧~!**
第四组:
** 🍑大家先想想吧~!**
第五组:
** 🍑大家先想想吧~!**
第六组:
** 🍑大家先想想吧~!**
第七组:
** 🍑大家先想想吧~!**
第八组:
** 🍑大家先想想吧~!**
总结:
数组名的意义:
- sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
- 除此之外所有的数组名都表示首元素的地址。
10.指针笔试题
**如果你能看到这里,相信指针对你来说已经了如指掌了,但是还是缺少实战,接下来我会列出几个笔试题,支持反复观看~!**
** 大家做题前好好想想,动手画画,可能这里的笔试题会是你将来面试题的原题或者经过改编的哦,大家再认真看完吧~!!!**
笔试题一:
** 🍑大家先想想吧~!**
笔试题二:
🍑大家先想想吧~!
笔试题三:
🍑大家先想想吧~!
笔试题四:
🍑大家先想想吧~!
笔试题五:
🍑大家先想想吧~!
笔试题六:
🍑大家先想想吧~!
笔试题七:
🍑大家先想想吧~!
11.小结
**本次内容纯干货,博主准备了好几天才整理好的,绝对够干~!![](https://img-blog.csdnimg.cn/c1115acffdef42edbfacbcd4e6604556.gif)**
** 可能看的时候会挺枯燥的,但是这点内容必须要掌握,而且要掌握好,只有掌握好这点才能正好的向下顺利学习~!**
** 其实最后还有一个笔试题我没写出来,那个有点复杂,我准备再出一篇博客,所以如果这点知识对你有帮助,麻烦你给博主个三连,博主才更有动力去总结并出讲解~!**
** 也不知道大家爱看干货还是喜欢实在的东西,大家如果有要求可以给我评论,都会看,都会看~!!! **
版权归原作者 Liyuyuea! 所有, 如有侵权,请联系我们删除。