当前位置: 首页 > news >正文

C语言学习系列-->看淡指针(3)

在这里插入图片描述

文章目录

  • 一、字符指针变量
  • 二、数组指针变量
    • 2.1 概述
    • 2.2 数组指针初始化
  • 三、二维数组传参本质
  • 四、函数指针
  • 五、typedef关键字
  • 六、函数指针数组

一、字符指针变量

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

一般使用:

#include<stdio.h>int main() {char ch = 'w';char* pc = &ch;return 0;
}

进阶使用

#include<stdio.h>
int main() {const char* pstr = "hello";printf("%s", pstr);return 0;
}

本质是把字符串 hello ⾸字符的地址放到了pstr中。
把⼀个常量字符串的⾸字符 h 的地址存放到指针变量 pstr 中。

经典例题:

#include<stdio.h>int main() {char str1[] = "hello word.";char str2[] = "hello word.";const char* str3 = "hello word.";const char* str4 = "hello word.";if (str1 == str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if (str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}

运行结果:

在这里插入图片描述

这⾥str3和str4指向的是⼀个同⼀个常量字符串。C/C++会把常量字符串存储到单独的⼀个内存区域,当⼏个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是⽤相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。

二、数组指针变量

2.1 概述

前面一篇文章我们讨论过指针数组,本质上是一个数组,一个存放指针的数组。

本节,我们要讨论的数组指针,后缀“指针”。

数组指针,指向的是数组的指针,存放的是数组的指针。

类比之前学的:

• 整形指针变量: int * pint; 存放的是整形变量的地址,能够指向整形数据的指针。
• 浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。

一般使用:

int (*p)[10];

解释:p先和*结合,说明p是⼀个指针变量变量,然后指着指向的是⼀个⼤⼩为10个整型的数组。所以p是⼀个指针,指向⼀个数组,叫 数组指针。

这⾥要注意:[]的优先级要⾼于号的,所以必须加上()来保证p先和结合。

2.2 数组指针初始化

数组指针变量是⽤来存放数组地址的,那怎么获得数组的地址呢?就是我们之前学习的 &数组名

int arr[10] = {0};
&arr;//得到的就是数组的地址

如果要存放个数组的地址,就得存放在数组指针变量中,如下:

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

在这里插入图片描述

我们调试也能看到 &arr 和 p 的类型是完全⼀致的。

int (*p) [10] = &arr;|    |    ||    |    ||    |   p指向数组的元素个数|   p是数组指针变量名p指向的数组的元素类型

三、二维数组传参本质

过去我们有⼀个⼆维数组的需要传参给⼀个函数的时候,我们是这样写的:

#include<stdio.h>void test(int arr[3][5], int r, int c) {int i = 0;int j = 0;for (i = 0; i < r; i++) {for (j = 0; j < c; j++) {printf("%d ", arr[i][j]);}printf("\n");}
}int main() {int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };test(arr, 3, 5);return 0;
}

⼆维数组起始可以看做是每个元素是⼀维数组的数组,也就是⼆维数组的每个元素是⼀个⼀维数组。那么⼆维数组的⾸元素就是第⼀⾏,是个⼀维数组。

所以,根据数组名是数组⾸元素的地址这个规则,⼆维数组的数组名表⽰的就是第⼀⾏的地址,是⼀维数组的地址。根据上⾯的例⼦,第⼀⾏的⼀维数组的类型就是 int [5] ,所以第⼀⾏的地址的类型就是数组指针类型 int(*)[5] 。那就意味着⼆维数组传参本质上也是传递了地址,传递的是第⼀⾏这个⼀维数组的地址,那么形参也是可以写成指针形式的。

在这里插入图片描述

#include<stdio.h>void test(int(*p)[5], int r, int c) {int i = 0;int j = 0;for (i = 0; i < r; i++) {for (j = 0; j < c; j++) {//printf("%d ", p[i][j]);printf("%d ", *(*(p + i) + j));}printf("\n");}
}int main() {int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };test(arr, 3, 5);return 0;
}

⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式。

四、函数指针

使用:

int (*pf3) (int x, int y)|    |        ------------ |    |             ||    |         pf3指向函数的参数类型和个数的交代|   函数指针变量名pf3指向函数的返回类型int (*) (int x, int y) //pf3函数指针变量的类型
#include <stdio.h>
int Add(int x, int y)
{return x+y;
}
int main()
{int(*pf3)(int, int) = Add;printf("%d\n", (*pf3)(2, 3));printf("%d\n", pf3(3, 5));return 0;
}

运行结果:

5
8

注意:&函数名和函数名都是函数的地址

经典例题:

练习1:

( *(void (*)())0 )();

这里:void( * )()是一个函数指针类型,指向一个没有参数,返回值为void类型的函数,这个类型被放在一个括号里,说明要进行强制类型转换,这里把0强制类型转换成(void (*)())型,此时0就成了一个地址,指向一个void ()型函数,然后再通过解引用找到这个函数,进行传参,当然这个函数没有参数,所以最后一个括号是空的

在这里插入图片描述

练习2:

void (*signal(int, void(*)(int))) (int);

signal是一个函数的函数名

声明signal函数有两个参数,第一个参数是int型,第二个参数是函数指针类型

这里signal函数的返回值类型是void(*)(int)

在这里插入图片描述

五、typedef关键字

typedef 是⽤来类型重命名的,可以将复杂的类型,简单化。

⽐如,你觉得 unsigned int 写起来不⽅便,如果能写成 uint 就⽅便多了,那么我们可以使⽤:

typedef unsigned int uint;
//将unsigned int 重命名为uint

如果是指针类型,能否重命名呢?其实也是可以的,⽐如,将 int* 重命名为 ptr_t ,这样写:

 typedef int* ptr_t;

但是对于数组指针和函数指针稍微有点区别:
⽐如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t ,那可以这样写:

typedef int(*parr_t)[5]; //新的类型名必须在*的右边

函数指针类型的重命名也是⼀样的,⽐如,将 void(*)(int) 类型重命名为 pf_t ,就可以这样写:

 typedef void(*pfun_t)(int);//新的类型名必须在*的右边

简化上述练习2的代码:

typedef void(*pf_t)(int);//把void(*)(int)型重命名成pf_t,注意pf_t的位置int main()
{pf_t signal(int, pf_t);return 0;
}

六、函数指针数组

把函数指针(地址)存到数组里面,就叫做函数指针数组

int (*parr1[3])();

parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢?
是 int (*)() 类型的函数指针。

在这里插入图片描述

http://www.lryc.cn/news/135246.html

相关文章:

  • Java抽象类详解
  • 06-微信小程序-注册程序-场景值
  • 多种方法实现 Nginx 隐藏式跳转(隐式URL,即浏览器 URL 跳转后保持不变)
  • 视频汇聚云平台EasyCVR视频监控管理平台进行SDN转推的操作步骤
  • SQL 语句继续学习之记录二
  • 【Python原创设计】基于Python Flask 机器学习的全国+上海气象数据采集预测可视化系统-附下载链接以及详细论文报告,原创项目其他均为抄袭
  • Unity进阶–通过PhotonServer实现人物选择和多人同步–PhotonServer(四)
  • 【Go 基础篇】Go语言获取用户终端输入:实现交互式程序的关键一步
  • 学习笔记:Opencv实现拉普拉斯图像锐化算法
  • 如何在前端实现WebSocket发送和接收UDP消息(多线程模式)
  • 【微服务】一文了解 Nacos
  • 量子计算对信息安全的影响:探讨量子计算技术对现有加密方法和信息安全基础设施可能带来的颠覆性影响,以及应对策略
  • ajax-axios-url-form-serialize 插件
  • 【AIGC】单图换脸离线版软件包及使用方法
  • 8.19论文阅读
  • HAProxy
  • 基于EasyExcel的Excel读取
  • 链路聚合详解
  • Shell编程学习之if分支语句的应用
  • 2023.8 - java - 泛型
  • 【数据结构练习】链表面试题锦集一
  • 自然语言处理从入门到应用——LangChain:链(Chains)-[通用功能:SequentialChain和TransformationChain]
  • 什么是卷积神经网络
  • 银行数字化转型程度-根据年报词频计算(2012-2021年)
  • 微信开发之一键修改群聊备注的技术实现
  • [oneAPI] 基于BERT预训练模型的SQuAD问答任务
  • 机器学习笔记之优化算法(十七)梯度下降法在强凸函数的收敛性分析
  • shell脚本中linux命令的特殊用法记录
  • Nvidia H100:今年55万张够用吗?
  • 【Vue2.0源码学习】生命周期篇-初始化阶段(initLifecycle)