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.mzph.cn/news/48773.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

钛合金为何成为iPhone 15 Pro材料首选?

多年来&#xff0c;iPhone Pro一直采用厚重的钢框架&#xff0c;但不会持续太久。 有了iPhone 15 Pro&#xff0c;苹果可能会从钢框架转向钛框架&#xff0c;这不仅仅是因为它听起来更酷。钛比钢有很多优点&#xff0c;尤其是它更轻&#xff0c;这将解决iPhone Pro与普通iPhon…

Kubernetes 安全机制 认证 授权 准入控制

客户端应用若想发送请求到 apiserver 操作管理K8S资源对象&#xff0c;需要先通过三关安全验证 认证&#xff08;Authentication&#xff09;鉴权&#xff08;Authorization&#xff09;准入控制&#xff08;Admission Control&#xff09; Kubernetes 作为一个分布式集群的管理…

ethers.js2:provider提供商

1、Provider类 Provider类是对以太坊网络连接的抽象&#xff0c;为标准以太坊节点功能提供简洁、一致的接口。在ethers中&#xff0c;Provider不接触用户私钥&#xff0c;只能读取链上信息&#xff0c;不能写入&#xff0c;这一点比web3.js要安全。 除了之前介绍的默认提供者d…

JAVA免杀学习与实验

1 认识Webshell 创建一个JSP文件&#xff1a; <% page import"java.io.InputStream" %> <% page import"java.io.BufferedReader" %> <% page import"java.io.InputStreamReader" %> <% page language"java" p…

jmeter进行业务接口并发测试,但登录接口只执行一次

业务接口性能测试&#xff0c;往往都是需要登录&#xff0c;才能请求成功&#xff0c;通常只需要登录一次&#xff0c;再对业务接口多次并发测试。 在测试计划中&#xff0c;添加setUp线程组 把登录请求放入到该线程组中&#xff0c;设置HTTP信息头&#xff0c;JSON提取(提取登…

前端基础(ES6 模块化)

目录 前言 复习 ES6 模块化导出导入 解构赋值 导入js文件 export default 全局注册 局部注册 前言 前面学习了js&#xff0c;引入方式使用的是<script s"XXX.js">&#xff0c;今天来学习引入文件的其他方式&#xff0c;使用ES6 模块化编程&#xff0c;…

电路学习+硬件每日学习十个知识点(40)23.8.20 (希腊字母读音,阶跃信号和冲激信号的关系式,信号的波形变换,信号的基本运算,卷积积分,卷积和)

文章目录 1.信号具有时间特性和频率特性。2.模拟转数字&#xff0c;抽样、量化、编码3.阶跃信号和冲激信号4.信号的波形变换&#xff08;时移、折叠、尺度变换&#xff09;5.信号的基本运算&#xff08;加减、相乘、微分与积分、差分与累加&#xff09;5.1 相加减5.2 相乘5.3 微…

基础论文学习(1)——ViT

Vision Transformer&#xff08;ViT&#xff09; 模型架构是在 ICLR 2021 上作为会议论文发表的一篇研究论文中介绍的&#xff0c;题为“An Image is Worth 16*16 Words: Transformers for Image Recognition at Scale”。它由Neil Houlsby&#xff0c;Alexey Dosovitskiy和Goo…

springMVC之视图

文章目录 前言一、ThymeleafView二、转发视图三、重定向视图四、视图控制器view-controller五、补充总结 前言 SpringMVC中的视图是View接口&#xff0c;视图的作用渲染数据&#xff0c;将模型Model中的数据展示给用户。 SpringMVC视图的种类很多&#xff0c;默认有转发视图和…

vscode远程调试

安装ssh 在vscode扩展插件搜索remote-ssh安装 如果连接失败&#xff0c;出现 Resolver error: Error: XHR failedscode 报错&#xff0c;可以看这篇帖子vscode ssh: Resolver error: Error: XHR failedscode错误_阿伟跑呀的博客-CSDN博客 添加好后点击左上角的加号&#xff0…

【Python机器学习】实验16 卷积、下采样、经典卷积网络

文章目录 卷积、下采样、经典卷积网络1. 对图像进行卷积处理2. 池化3. VGGNET4. 采用预训练的Resnet实现猫狗识别 TensorFlow2.2基本应用5. 使用深度学习进行手写数字识别 卷积、下采样、经典卷积网络 1. 对图像进行卷积处理 import cv2 path data\instance\p67.jpg input_…

Linux 线程同步——条件变量

一、条件变量的概念 如果说互斥锁是用于同步线程对共享数据的访问的话&#xff0c;那么条件变量则是用于在线程之间同步共享数据的值。条件变量提供了一种线程间的通知机制&#xff1a;当某个共享数据达到某个值的时候&#xff0c;唤醒等待这个共享数据的线程。如下图所示&…

vue3中使用第三方插件mitt实现任意组件通讯

vue3中使用第三方插件mitt实现任意组件通讯 组件通讯是vue3组合式开发的核心之一&#xff0c;现在我在写代码时&#xff0c;一个组件的代码超过了200行&#xff0c;基本都会拆分组件。组件拆分后&#xff0c;组件之间的通讯就很重要&#xff0c;总结了一下&#xff0c;目前有这…

【SQL应知应会】索引(三)• MySQL版:聚簇索引与非聚簇索引;查看索引与删除索引;索引方法

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 本文收录于SQL应知应会专栏,本专栏主要用于记录对于数据库的一些学习&#xff0c;有基础也有进阶&#xff0c;有MySQL也有Oracle 索引 • MySQL版 前言一、索引1.简介2.索引类型之逻…

【李沐】3.2线性回归从0开始实现

%matplotlib inline import random import torch from d2l import torch as d2l1、生成数据集&#xff1a; 看最后的效果&#xff0c;用正态分布弄了一些噪音 上面这个具体实现可以看书&#xff0c;又想了想还是上代码把&#xff1a; 按照上面生成噪声&#xff0c;其中最后那…

利用大模型反馈故障的解决方案

背景 观测云有两个错误巡检脚本&#xff0c;RUM 错误巡检和 APM 错误巡检&#xff0c;代码均开源。 错误巡检的主要目的是发现新出现的错误消息(error stack)&#xff0c;原有的巡检在上报了相应的事件报告后&#xff0c;只是定位了问题&#xff0c;并没有给出合适的解决方案。…

Pandas基础知识

文章目录 Pandas的数据结构Series --- 由数据和索引组成&#xff08;索引&#xff08;index&#xff09;在左&#xff0c;数据&#xff08;values&#xff09;在右&#xff09;DataFrame --- 索引包括行索引和列索引&#xff0c;每列数据可以是不同的类型 Pandas的索引操作 ---…

SpringMVC拦截器学习笔记

SpringMVC拦截器 拦截器知识 拦截器(Interceptor)用于对URL请求进行前置/后置过滤 Interceptor与Filter用途相似但实现方式不同 Interceptor底层就是基于Spring AOP面向切面编程实现 拦截器开发流程 Maven添加依赖包servlet-api <dependency><groupId>javax.se…

nginx代理webSocket链接,webSocket频繁断开重连

一、场景 1、使用nginx代理webSocket链接&#xff0c;消息发送和接收都是正常的&#xff0c;但webSocket链接会频繁断开重连 2、如果不使用nginx代理则一切正常 3、程序没有做webSocket心跳处理 如下图 二、nginx代理配置 upstream cloud_ass {#ip_hash;server 192.168.1.…

2023年国赛数学建模思路 - 案例:随机森林

文章目录 1 什么是随机森林&#xff1f;2 随机深林构造流程3 随机森林的优缺点3.1 优点3.2 缺点 4 随机深林算法实现 建模资料 ## 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 什么是随机森林&#xff…