最全C语言深度剖析指针,一次哔哩哔哩面试经历

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

int arr[10];
int(*p)[10] = &arr; //&arr为整个数组的地址
return 0;

}


#### 2、3、2 数组名和&数组名的区别



> 
>   我们看如下数组:
> 
> 
> 
> ```
> int arr[10];
> ```
> 
>   arr 和 &arr 分别是啥?我们知道arr是数组名,数组名表示数组首元素的地址。那&arr数组名到底是啥?我们看一段代码:
> 
> 
> 



#include <stdio.h>
int main()
{
int arr[10] = {0};
printf(“%p\n”, arr);
printf(“%p\n”, &arr);
return 0;
}



> 
>   上述代码的运行结果是什么呢?我们看下图:
> 
> 
> ![](https://img-blog.csdnimg.cn/a55b79f02ddb415fb9aac81fc11c9ed2.png)
> 
> 
>   从这里我们好像看不出来有什么区别。那我们再看一下下面的代码:
> 
> 
> 



#include <stdio.h>
int main()
{
int arr[10] = { 0 };
printf(“arr = %p\n”, arr);
printf(“&arr= %p\n”, &arr);
printf(“arr+1 = %p\n”, arr+1);
printf(“&arr+1= %p\n”, &arr+1);
return 0;
}



> 
>   上面代码的运行结果呢?如果我们不了解两者的区别的话,就很容易出错。我们看下图运行结果:![](https://img-blog.csdnimg.cn/c8ff888576c4425ca84220c6fda5bd70.png)
> 
> 
>    根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是**意义应该不一样的。**实际上: **&arr 表示的是数组的地址,而不是数组首元素的地址。**(细细体会一下)本例中 &arr 的类型是:int(\*)[10] ,是一种数组指针类型数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40。
> 
> 
> 


#### 2、3、3 数组指针的用法



> 
>   我们直接看一段数组指针的代码:
> 
> 
> 



#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)
{
int i = 0;
for(i=0; i<row; i++)
{
for(j=0; j<col; j++)
{
printf(“%d “, arr[i][j]);
}
printf(”\n”);
}
}
void print_arr2(int (*arr)[5], int row, int col)
{
int i = 0;
for(i=0; i<row; i++)
{
for(j=0; j<col; j++)
{
printf(“%d “, arr[i][j]);
}
printf(”\n”);
}
}
int main()
{
int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
print_arr1(arr, 3, 5);
//数组名arr,表示首元素的地址
//但是二维数组的首元素是二维数组的第一行
//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
//可以数组指针来接收
print_arr2(arr, 3, 5);
return 0;
}



> 
>    我们再来看一下自己的掌握程度。看看是否能够解释一下如下代码的意思。
> 
> 
> 



int arr[5]; //数组

int parr1[10]; //指针数组——parr1是一个数组,该数组有十个元素,每个元素的类型为int

int (*parr2)[10]; //数组指针——parr2是一个指针,该指针是指向的一个数组,
//指向的数组的元素个数为10个,数组元素的类型为int

int (*parr3[10])[5]; //parr3是一个数组,该数组有10个元素,每个元素的类型为数组指针,
//也就是每个元素为指针,指针指向的是一个数组。


### 2、4 函数指针


#### 2、4、1 函数指针的解释



> 
>   我们先来看一段代码:
> 
> 
> 



#include <stdio.h>
void test()
{
printf(“hehe\n”);
}
int main()
{
printf(“%p\n”, test);
printf(“%p\n”, &test);
return 0;
}



> 
>    看到这段代码的首先反应是:函数有地址吗?答案是有的。我们先看一下运行的结果,如下图:![](https://img-blog.csdnimg.cn/e552e4220ff24906aeb632a58131afb4.png)
> 
> 
>    通过上述例子,我们更加确定了函数确实是有地址的。并且**函数名和&函数名都是函数的地址,两者是相同的**。那我们怎么保存函数的地址呢?肯定是需要指针来保存的。根据我们上面的学习,这里就可以推断出**保存函数的地址是需要一个函数指针。**
> 
> 
> 我们通过上面的例子来说明函数指针的使用,如下:
> 
> 
> 



#include <stdio.h>
void test()
{
printf(“hehe\n”);
}
int main()
{
void (*p)()=test; //指针p即为函数指针,存储的是test函数的地址。
printf(“%p\n”, p);
printf(“%p\n”, test);
return 0;
}


#### 2、4、2 函数指针的举例分析



> 
>   通过对上面的函数指针的了解后,我们分析一下下面的两段代码的意思:
> 
> 
> 



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



> 
>   **代码1**:我们可以从最里面的括号进行分析。**void(\*)()**是一个函数指针类型,**(void ( \* )( ))0**的意思是**将0强制类型转换为一个函数指针类型****地址**。最后再对**以0为地址进行解引用操作**,相当于**调用了以0为地址的函数**。
> 
> 
>   **代码2**:我们可以从变量名称signal入手。我们可以分析出**signal(int , void(\*)(int))**是一次函数的调用,剩下的**void (\*)(int)**即为signal函数调用的**返回值的类型**,该类型是一个指针,该指针的类型是一个函数指针。
> 
> 
> 


### 2、5 函数指针数组


#### 2、5、1 函数指针的解释



> 
>   学完函数指针后,我们学函数指针数组也就容易了。**函数指针数组是一个数组,该数组存放的数据类型为函数指针。**
> 
> 
> 把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?我们看如下代码:
> 
> 
> 



int (*parr1[10])();



> 
>    上述的代码就是一个函数指针数组。我们来分析一下。上述的**parr1与[10]结合,表明parr1是一个数组**,该数组的元素类型为**int(\*)()**,很明显了元素的类型为**函数指针**。
> 
> 
> 


#### 2、5、2 函数指针的引用举例



> 
>   当我们有多个函数,且函数的参数和返回值类型相同的时候,我们就可以将多个函数的地址放到一个函数指针数组中。
> 
> 
>   我们要写出一段代码,要求该代码能够实现加、减、乘、除算法。我们先看如下代码。
> 
> 
> 



#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 ab;
}
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”);
breark;
default:
printf( “选择错误\n” );
break;
}
} while (input);
return 0;
}



> 
>    我们发现,上面的代码switch语句中,代码有大量的重复和冗余。这时我们可以用函数指针数组来代替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 ab;
}
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



> 
>   这是我们发现,代码的简洁性提高了很多。 
> 
> 
> 


### 2、6 指向函数指针数组的指针



> 
>   指向函数指针数组的指针是什么呢?一看到这个名字就头昏脑胀,感觉很难理解。我们不妨一步一步分析一下。首先我们可以很容易看出**指向函数指针数组的指针是一个指针**。**指针指向的类型是什么呢?**指针指向的是**一个数组,该数组存放的数据类型是函数指针**。
> 
> 
>   如何定义呢?我们看如下代码:
> 
> 
> 



void test(const char* str)
{
printf(“%s\n”, str);
}
int main()
{
//函数指针pfun
void (pfun)(const char) = test;

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

们看如下代码:

void test(const char* str)
{
    printf("%s\n", str);
}
int main()
{
    //函数指针pfun
    void (*pfun)(const char*) = test;


[外链图片转存中...(img-v9fRAO2i-1715814572140)]
[外链图片转存中...(img-CtvMSCs7-1715814572141)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

  • 13
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值