你了解C语言中的数组指针和函数指针吗?

news/2024/10/11 15:22:33/

如题,本篇文章重点讲解C语言中的数组指针和函数指针。这2种指针其实都不是很常用,个人感觉使用起来代码的可读性不是很高,但是还是需要了解一下。
数组指针&&函数指针

数组指针

数组指针,即指向数组的指针,是用来存放数组的地址的。那如何取出数组的地址呢?直接&数组名即可。如:

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
&arr; // 取出数组的地址

这里的&arr,就能得到数组的地址。如果我们想把它存起来,就需要一个数组指针变量。写法如下:

int (*p)[10] = &arr;

解释一下。首先,括号是不能省略的。如果写成int *p[10],就是个指针数组了,这个数组可以存放10个整形指针变量。

括号把*和数组指针变量名p括起来,此时p会优先和*结合,就是一个指针。把(*p)去掉,剩下的int [10]就是数组的类型,表示这个数组指针变量指向的数组存储int类型的数据,容量是10。

这个数组指针类型是:把变量名p去掉后的int (*)[10]

数组指针应该如何使用呢?这里举一个二维数组传参的例子。比如,我们有一个三行五列的二维数组,请你写一个函数,使用指针来打印这个数组里的值。

int arr[3][5] = {{1,2,3,4,5}, {11,22,33,44,55}, {111,222,333,444,555}};
Print(arr, sizeof(arr)/sizeof(arr[0]), sizeof(arr[0])/sizeof(arr[0][0]));

函数Print的形参应该如何写呢?如果是用一个二维数组接收,我相信大家应该是会写的,但是如果要用指针来接收呢?这个指针是什么类型的呢?

数组传参,数组名表示首元素的地址,二维数组的首元素就是第一行!也就是说,二维数组arr的数组名表示第一行的地址,而第一行是一个含有5个int的一维数组。再换句话说,arr作为二维数组的数组名,表示的是一个含有5个int的一维数组的地址,所以需要一个数组指针来接收,这个数组指针指向一个含有5个int的一维数组!

void Print(int (*p)[5], int r, int c)
{}

函数体实现的关键就是,如何使用数组指针p来遍历原来的二维数组arr。其实呀,既然是数组名作为参数传过来,就可以当成正常的数组那样使用,比如p[i][j]其实就表示第i行第j列的元素。在C语言中,a[i]就等价于*(a+i),所以p[i][j]就等价于*(*(p+i)+j),所以以下2种实现是等价的。

void Print(int(*p)[5], int r, int c)
{for (int i = 0; i < r; ++i){for (int j = 0; j < c; ++j){// 写法1//printf("%d ", p[i][j]);// 写法2printf("%d ", *(*(p + i) + j));}printf("\n");}
}

如果用数组指针的角度来理解*(*(p+i)+j),应该怎么想呢?p是数组第一行的地址,p+i就是数组第i行的地址,*(p+i)就拿到了数组的第i行,相当于拿到了数组第i行的数组名,也就是数组第i行第一个元素的地址。那么,*(p+i)+j就是数组第i行第j个元素的地址,*(*(p+i)+j)就是数组第i行第j个元素。

函数指针

函数指针和数组指针非常像。数组指针是指向数组的指针,函数指针则是指向函数的指针。也就是说,函数指针是用来存放函数的地址的。

如何取得函数的地址呢?有2中方法:

  1. 函数名就表示函数的地址。
  2. &函数名也表示函数的地址。

比如:

int Add(int x, int y)
{return x + y;
}

对于Add函数,Add&Add都能得到它的地址。那么,存放这个地址的函数指针应该如何写呢?

int (*p1)(int, int) = Add;
int (*p2)(int, int) = &Add;

和数组指针非常类似。用括号把*p括起来,表示p是一个指针,去掉(*p)后得到的int (int, int)就是这个指针指向的函数的类型,即参数是int, int,返回类型是int的函数。

这个函数指针类型名是:把变量名去掉后的int (*)(int, int)

使用函数指针可以调用函数,有2种调用方式:

  1. 不解引用,直接调用:p(3, 5)
  2. 解引用后再调用:(*p)(3, 5)

比如,以上的代码可以这么写:

int (*p)(int, int) = Add;
int ret1 = p(3, 5);
int ret2 = (*p)(3, 5);

其实这个解引用操作没啥用,你哪怕写int ret2 = (***************p)(3, 5);,效果和不加*是一样的。

总结

  1. 数组指针是指向数组的指针,数组指针类型由3部分构成:用括号把变量名和*括起来表示是一个指针,左边写指针指向的数组存储的元素的类型,右边用方括号表示指向的数组的容量。
  2. 函数指针是指向函数的指针,函数指针类型由3部分构成:用括号把变量名和*括起来表示是一个指针,左边写指针指向的函数的返回类型,右边用圆括号表示函数的形参列表。
  3. 这两货确实挺像。

感谢大家的阅读!


http://www.ppmy.cn/news/47034.html

相关文章

车载网络 - Autosar网络管理 - 跳转状态

四、Autosar网络管理跳转状态 网络模式对应报文状态 Autosar网络管理报文各个状态对应的网络管理报文和应用报文的发送和接收状态。 网络模式 网络管理报文 应用报文 收发类型 发送报文 接收报文 发送报文 接收报文 总线睡眠模式(BSM) No Yes No NA 准备总线睡眠模…

第14届蓝桥杯 | 冶炼金属

作者&#xff1a;指针不指南吗 专栏&#xff1a;第14届蓝桥杯真题 &#x1f43e;慢慢来&#xff0c;慢慢来&#x1f43e; 文章目录 题目代码摸索第一次 AC 5/10第二次 AC 100% 反思 题目 链接&#xff1a; 4956. 冶炼金属 - AcWing题库 小蓝有一个神奇的炉子用于将普通金属 O …

老宋 带你五分钟搞懂vue

Vue 1.1 什么是框架 任何编程语言在最初的时候都是没有框架的&#xff0c;后来随着在实际开发过程中不断总结『经验』&#xff0c;积累『最佳实践』&#xff0c;慢慢的人们发现很多『特定场景』下的『特定问题』总是可以『套用固定解决方案』。于是有人把成熟的『固定解决方案…

javascript之函数

什么是函数&#xff1f; &#xff08;函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块。&#xff09; 是封装了一段可以重复调用执行的代码&#xff0c;通过找个代码块&#xff0c;能够实现大量代码的重复使用 使用函数的方式&#xff1a; 声明函数调用函数 声…

使用vscode+cmake进行c++代码编写

1. 前言 因为vcode的主题格式比visual studio好看&#xff0c;而且注释使用ctr/注释非常方便。所以对于一下小型的c代码测试&#xff0c;例如用不到外部库&#xff0c;只需要纯c自己语法&#xff0c;我就想和python一样&#xff0c;在vscode上写。因此记录一下比较简单的典型的…

C++算法初级10——动态规划

C算法初级10——动态规划 文章目录 C算法初级10——动态规划最优化问题动态规划分析流程和条件 最优化问题 生活中我们常常遇到这样一些问题&#xff1a; 看到上面的例子&#xff0c;我们发现这些问题都是在最大化&#xff08;或者最小化&#xff09;某个指标&#xff1a;最小…

剪枝与重参第七课:YOLOv8剪枝

目录 YOLOv8剪枝前言1.Overview2.Pretrain(option)3.Constrained Training4.Prune4.1 检查BN层的bias4.2 设置阈值和剪枝率4.3 最小剪枝Conv单元的TopConv4.4 最小剪枝Conv单元的BottomConv4.5 Seq剪枝4.6 Detect-FPN剪枝4.7 完整示例代码 5.YOLOv8剪枝总结总结 YOLOv8剪枝 前…

你真的会用iPad吗,如何使iPad秒变生产力工具?在iPad上用vscode写代码搞开发

目录 前言 视频教程 1. 本地环境配置 2. 内网穿透 2.1 安装cpolar内网穿透(支持一键自动安装脚本) 2.2 创建HTTP隧道 3. 测试远程访问 4. 配置固定二级子域名 4.1 保留二级子域名 4.2 配置二级子域名 5. 测试使用固定二级子域名远程访问 6. iPad通过软件远程vscode…