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

第四站:指针的进阶-(二级指针,函数指针)

目录

二级指针

二级指针的用途

多级指针的定义和使用

指针和数组之间的关系

存储指针的数组(指针数组:保存地址值)

指向数组的指针(数组指针)

传参的形式(指针)

数组传参时会退化为指针

 void类型的指针

函数指针

定义:

调用:两种方式:(*指针名)(参数地址)  或者  指针名(参数地址)

应用:


二级指针

二级指针也是一个普通的指针变量,只是它里面保存的值是另外一个一级指针的地址

#include <iostream>using namespace std;
int main(void) {int massage = 888;int* guizi1 = &massage;int** guizi2 = &guizi1;cout << "massage的地址" << &massage << endl;cout << "guizi1内的地址" << guizi1 << endl;cout << "guizi2内的地址" << *guizi2 << endl;cout << "guizi1内的值" << *guizi1 << endl;cout << "guizi2内的值" << **guizi2 << endl;return 0;
}

二级指针和一级指针的类型都是相同的,

二级指针所指向的对象就是一级指针的值,一级指针的值又是指向的变量的值,所以不管有多少级指针,他们只要保存的是上一级指针的地址,那么内部所保存的地址值都是一样的,

这里不要和指针变量本身作为一个变量的指针混淆了,所定义的二级指针和一级指针他们的地址值肯定是不同的但是他们内部所保存的内容(对象的地址)是相同的

二级指针的用途

普通指针可以将变量通过参数“带入”函数内部然后进行修改,但没办法将函数的内部变量“带出”函数

二级指针不但可以将变量通过参数带入函数内部,也可以将函数内部变量 “带出”到函数外部。

 使用的一级指针报警:读取位置 0x0000000000000000 时发生访问冲突。这个全部为0的地址就是给一级指针初始化时设的NULL值(空指针,就是值为 0 的指针,任何程序数据都不会存储在地址为 0 的内存块中,它是被操作系统预留的内存块)

 

#include <iostream>using namespace std;void swap(int* a, int* b,int *c) {int tmp = *a;*a = *b;*b = tmp;int d = *a + *b;c = &d;}void boy(int** meipo) {static int boy = 22;*meipo = &boy;
}
int main(void) {int a = 8;int b = 3;int* c = NULL;swap(&a, &b, c);cout << "一级指针传入之后c的值:"<<endl;int* type = NULL;boy(&type);cout << "通过二级指针带回来的值:" << *type << endl;return 0;
}

通过程序调用可以看出给二级指针传入地址值后,二级指针可以通过一级指针把函数内部的变量地址值给带回来

多级指针的定义和使用

#include <iostream>using namespace std;
int main(void) {int massage = 888;int* guizi1 = &massage;int** guizi2 = &guizi1;int*** guizi3 = &guizi2;int**** guizi4 = &guizi3;int***** guizi5 = &guizi4;cout << "massage的地址" << &massage << endl;cout << "guizi1内的地址" << guizi1 << endl;cout << "guizi2内的地址" << *guizi2 << endl;cout << "guizi3内的地址" << **guizi3 << endl;cout << "guizi4内的地址" << ***guizi4 << endl;cout << "guizi5内的地址" << ****guizi5 << endl;cout << "guizi1内的值" << *guizi1 << endl;cout << "guizi2内的值" << **guizi2 << endl;cout << "guizi3内的值" << ***guizi3 << endl;cout << "guizi4内的值" << ****guizi4 << endl;cout << "guizi5内的值" << *****guizi5 << endl;return 0;
}

指针和数组之间的关系

#include <iostream>using namespace std;void print_1(int days[], int len) {for (int i = 0; i < len; i++) {//用数组的方式输出cout << "用数组的方式输出:" << days[i] << " ";//用指针的形式输出cout << "用指针的方式输出:" << *(days + i);cout << endl;}
}
void print_2(int* day, int len) {for (int i = 0; i < len; i++) {//用数组的方式输出cout << "用数组的方式输出:" << day[i] << " ";//用指针的形式输出cout << "用指针的方式输出:" << *(day + i);cout << endl;}
}
int main(void) {int days[] = { 31,28,31,30,31,30,31,31,30,31,30,31 };int* day = NULL;int len = sizeof(days) / sizeof(int);day = days;//for (int i = 0; i < len; i++){//	//用数组的方式输出//	cout << "用数组的方式输出:" << days[i] <<" ";//	//用指针的形式输出//	cout << "用指针的方式输出:" << *(day + i);//	cout << endl;//}//当传入参数是数组时cout << "当传入参数是数组时:" << endl;print_1(days, len);cout << "当传入参数是指针时:" << endl;print_2(day, len);return 0;
}

存储指针的数组(指针数组:保存地址值)

定义: 类型 *指针数组名[元素个数] ;如:int *p[2]

定义一个有n个元素个数指针数组,每个元素都是一个指针变量

指针数组是先有地址值,然后再通过地址值,指针数组本质可以理解为不是一个指针而是一个只保存地址的数组,在操作指针数组时,这个指针数组是先保存有这个对象的地址(并不是指向这个对象),然后可以通过地址访问这个对象的值

#include <iostream>using namespace std;int main(void) {int days[] = { 31,28,31,30,31,30,31,31,30,31,30,31 };int len = sizeof(days) / sizeof(int);int* p[1];//代表这个指针数组内,有一个指针变量p[0] = days;for (int i = 0; i < len; i++){if (*p[0] > days[i]) {p[0] = &days[i];}}cout << *p[0] << endl;return 0;
}

指向数组的指针(数组指针)

1. 指向数组的指针

int (*p)[3]; //定义一个指向三个成员的数组的指针,数组指针的个数,和数组的列数是对应的

访问元素的两种方式:

数组法: (*p)[j]

指针法: *((*p)+j)

#include <iostream>using namespace std;int main(void) {int days[4][3] = {31,28,31,30,31,30,31,31,30,31,30,31};//几维的数据就将元素设为几个元素的指针int (*p)[3];//定义一个指针数组,指向4个元素的的数组的指针int* little = NULL;//使用数组型的指针型数组访问p = &days[0];for (int i = 0; i < 4; i++){for (int j = 0; j < 3; j++){cout << (*p)[j] << " ";}p++;}cout << endl;//使用指针型的指针数组访问p = &days[0];little = &(*p)[0];for (int i = 0; i < 4; i++) {for (int j = 0; j < 3; j++) {if (*little > *((*p) + j)) {little = (*p) + j;}}p++;}cout << *little << endl;return 0;
}

传参的形式(指针)

数组传参时会退化为指针

1)退化的意义:C 语言只会以值拷贝的方式传递参数,参数传递时,如果拷贝整个数组,效率会大大降低,并且参数将位于栈上,太大的数组拷贝将会导致栈溢出。

2)因此,C 语言将数组的传参进行了退化。将整个数组拷贝一份传入函数时,将数组名看做常量指针,传数组首元素的地址。(这样参数在进行赋值时,就会和这个数组共用同一个内存空间)

#include <iostream>using namespace std;
//方法1
void method_1(int days[12]) {for (int i = 0; i < 12; i++){cout << days[i] << " ";}
}
//方法2
void method_2(int days[], int len) {for (int i = 0; i < len; i++) {cout << days[i] << " ";}
}
//方法3
void method_3(int* array, int len) {for (int i = 0; i < len; i++) {cout << *(array+i) << " ";}
}
//方法4
void method_4(int* arr[]) {for (int i = 0; i < 12; i++) {cout << *arr[i] << " ";}
}
//方法5
void method_5(int* arr[], int len) {for (int i = 0; i < len; i++) {cout << *arr[i] << " ";}
}
//方法6
void method_6(int** arr, int len) {for (int i = 0; i < len; i++) {cout << *arr[i] << " ";}
}
//方法7
void method_7(int(*k)[12]) {for (int i = 0; i < 12; i++) {cout << *(*k+i) << " ";}
}
int main(void) {//数组作为参数传递,输出数组的6种方式int days[12] = {31,28,31,30,31,30,31,31,30,31,30,31};int len = sizeof(days) / sizeof(int);//方法一;cout << "方法一:将数组作为形参参接收数组:" << endl;method_1(days);cout << endl;cout << "方法二:将数组作为形参参接收数组,并指定数组的元素个数:" << endl;method_2(days, len);cout << endl;cout << "方法三:使用指针作为形参接受数组,并指定数组的元素个数:" << endl;method_3(days, len);cout << endl;cout << "方法四:使用指针数组作为形参:" << endl;//先为指针数组内的元素赋值int* day[12] = {0};//初始化,指针数组时先作为一个数组for (int i = 0; i < len; i++){day[i] = &days[i];}method_4(day);cout << endl;cout << "方法五:使用指针数组作为形参,并指定数组的元素个数:" << endl;method_5(day, len);cout << endl;cout << "方法六:使用二级指针作为形参,并指定数组的元素个数:" << endl;method_6(day, len);cout << endl;int(*k)[12];k = &days;cout << "方法七:使用数组指针作为形参接受数组:" << endl;method_7(k);return 0;
}

 void类型的指针

void => 空类型

void* => 空类型指针,只存储地址的值,丢失类型,无法访问,要访问其值,我们必须对这个指 针做出正确的类型转换(其他类型赋值给void指针,会自动转,void的指针赋值给其他类型则需要强制转换),然后再间接引用指针。

void*类型的指针是无法进行算数运算的,同样无法访问(E0852    表达式必须是指向完整对象类型的指针)

#include <iostream>using namespace std;int main(void) {int a = 10;int* p = &a;void* p1 = &a;void* p2 = p; //其他类型的指针赋值给void类型的指针自动转换p++;//有具体类型的指针可以算术运算//p1++;//错误int* p3 = (int*)p1;//void类型的指针赋值给其他类型的指针需要进行强制转换//cout << *p1 << endl;//E0852:表达式必须是指向完整对象类型的指针cout << p1 << endl;//可以输出他的地址值cout << p2 << endl;cout << *p3 << endl;return 0;
}

函数指针

函数指针简单理解就是把整个函数当做一个指针

定义:

把函数声明移过来,把函数名改成 (* 函数指针名)

例如:

定义一个函数为:int man(const void *a,const void *b){}

函数的指针定义为:int (*p)(const void *a ,const void *b);        

最后将函数的地址赋值给指针:p = &man;

调用:
两种方式:(*指针名)(参数地址)  或者  指针名(参数地址)

例如上述:(*p)(&a,&b); 或者 p(&a,&b);

#include <iostream>using namespace std;
int compare( void* a, void* b) {int* x = (int*)a;int* y = (int*)b;return *x - *y;}int main(void) {int arr[] = { 2, 10, 30, 1, 11, 8, 7,25 };int len = sizeof(arr) / sizeof(int);int (*p)( void* a, void* b);p = &compare;cout << arr[0]  << " " << arr[1] << endl;cout << p(&arr[0], &arr[1]) << endl;return 0;
}

应用:

使用VS自带的快速排序函数,来调用自定函数进行排序

#include <iostream>using namespace std;
int compare(const void* a,const void* b) {int *x = (int*)a;int *y = (int*)b;return *x - *y;//小于0,升序排序,
}int main(void) {int arr[] = { 2, 10, 30, 1, 11, 8, 7,25 };int len = sizeof(arr) / sizeof(int);cout << "排序前:" << endl;for (int  i = 0; i < len; i++){cout << arr[i] << " ";}int (*p)(const void* a,const void* b);p = &compare;//这里qsort函数的第四个参数需要的函数参数类型为const类型的指针qsort(arr, len, sizeof(int), p);cout << endl;//这个作用是换行cout <<"排序后:" << endl;for (int i = 0; i < len; i++) {cout << arr[i] << " ";}return 0;
}

相关文章:

第四站:指针的进阶-(二级指针,函数指针)

目录 二级指针 二级指针的用途 多级指针的定义和使用 指针和数组之间的关系 存储指针的数组(指针数组:保存地址值) 指向数组的指针(数组指针) 传参的形式(指针) 数组传参时会退化为指针 void类型的指针 函数指针 定义: 调用:两种方式:(*指针名)(参数地址) 或者 指针…...

浏览器渲染原理(面试重点)

一、浏览器是如何渲染页面的 常见的简洁答案&#xff1a; 浏览器内核拿到内容后&#xff0c;渲染流程大致如下&#xff1a;解析HTML&#xff0c;构建Dom树&#xff1b;解析CSS&#xff0c;构建Render树&#xff1b;&#xff08;将CSS代码解析成树形的数据结构&#xff0c;与D…...

C //练习 5-3 用指针方式实现第2章中的函数strcat。函数strcat(s, t)将t指向的字符串复制到s指向的字符串的尾部。

C程序设计语言 &#xff08;第二版&#xff09; 练习 5-3 练习 5-3 用指针方式实现第2章中的函数strcat。函数strcat(s, t)将t指向的字符串复制到s指向的字符串的尾部。 注意&#xff1a;代码在win32控制台运行&#xff0c;在不同的IDE环境下&#xff0c;有部分可能需要变更。…...

深度剖析Redis:从基础到高级应用

目录 引言 1、 Redis基础 1.1 Redis数据结构 1.1.1 字符串&#xff08;String&#xff09; 1.1.2 列表&#xff08;List&#xff09; 1.1.3 集合&#xff08;Set&#xff09; 1.1.4 散列&#xff08;Hash&#xff09; 1.1.5 有序集合&#xff08;Sorted Set&#xff09;…...

视频监控录像服务器(中心录像服务器)功能详细介绍

目 录 一、概述 &#xff08;一&#xff09;定义 &#xff08;二&#xff09;视频监控中心录像服务器 二、存储策略服务 &#xff08;一&#xff09;存储策略配置 1、 录入页面 2、 选择需要进行录像的视频 3、批量选择多个通道号 4、其他关键参数…...

SouthernBiotech抗荧光淬灭封片剂

荧光淬灭又称荧光熄灭或萃灭&#xff0c;是指导致特定物质的荧光强度和寿命减少的所有现象。引起荧光淬灭的物质称为荧光淬灭剂。SouthernBiotech专门开发的Fluoromount-G系列荧光封片剂是以甘油为基础&#xff0c;加入抗荧光淬灭剂&#xff0c;可明显降低荧光淬灭现象&#xf…...

[Excel]如何找到非固定空白格數列的條件數據? 以月份報價表單為例

在群組中看到上述問題&#xff0c;研判應是一份隨月份變動的產品報價表單&#xff0c;空白欄可能表示該月份價格與上個月份一致。這個問題是需要取得最近一次單價和倒數第二次單價&#xff0c;常用且實務的excel案例值得紀錄。 最近一次單價: INDEX($B2:$G2,1,LARGE(IF(ISBLAN…...

TypeScript进阶(二)深入理解装饰器

✨ 专栏介绍 TypeScript是一种由微软开发的开源编程语言&#xff0c;它是JavaScript的超集&#xff0c;意味着任何有效的JavaScript代码都是有效的TypeScript代码。TypeScript通过添加静态类型和其他特性来增强JavaScript&#xff0c;使其更适合大型项目和团队开发。 在TypeS…...

书生·浦语第三次作业

我最近在参加书生浦语大模型实战营&#xff0c;这是第三次作业打卡&#xff01; 如果你也想两周玩转大模型微调&#xff0c;部署与测评全链路。报名链接&#xff1a;invite 书生浦语大模型实战营报名 邀请码可以填026014 一、基础作业&#xff1a;复现课程知识库助手搭建过程…...

GPT实战系列-LangChain + ChatGLM3构建天气查询助手

GPT实战系列-LangChain ChatGLM3构建天气查询助手 用ChatGLM的工具可以实现很多查询接口和执行命令&#xff0c;而LangChain是很热的大模型应用框架。如何联合它们实现大模型查询助手功能&#xff1f;例如调用工具实现网络天气查询助手功能。 LLM大模型相关文章&#xff1a; …...

LeetCode 2696.删除子串后的字符串最小长度:栈

【LetMeFly】2696.删除子串后的字符串最小长度&#xff1a;栈 力扣题目链接&#xff1a;https://leetcode.cn/problems/minimum-string-length-after-removing-substrings/ 给你一个仅由 大写 英文字符组成的字符串 s 。 你可以对此字符串执行一些操作&#xff0c;在每一步操…...

Xcode15 升级问题记录

这里写自定义目录标题 新版本Xcode15升级问题1&#xff1a;rsync error: some files could not be transferred (code 23) at ...参考 新版本Xcode15升级 下载地址&#xff1a;https://developer.apple.com/download/all/ 我目前使用的版本是Xcode15.2 我新创建了一个项目&…...

List、Set、Map有什么区别?

List、Set和Map是Java中的三种基本数据结构&#xff0c;它们在元素重复性、有序性和用途方面存在显著的区别。 元素重复性&#xff1a; List允许有重复的元素。任何数量的重复元素都可以在不影响现有重复元素的值及其索引的情况下插入到List集合中。 Set集合不允许元素重复。…...

centOS系统yum安装和卸载mongodb

0.1 什么是mongodb&#xff1f; 0.2 Mongodb是一个基于分布式文件存储的数据库。由C语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。 0.3 Mongodb是一个介于关系数据库和非关系数据库之间的产品&#xff0c;是非关系数据库当中功能最丰富&#xff0c;最像关系数据…...

2023年12月 C/C++(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

C/C++编程(1~8级)全部真题・点这里 第1题:数的输入和输出 输入一个整数和双精度浮点数,先将浮点数保留2位小数输出,然后输出整数。 时间限制:1000 内存限制:65536 输入 一行两个数,分别为整数N(不超过整型范围),双精度浮点数F,以一个空格分开。 输出 一行两个数,分…...

Python爬虫---Scrapy项目的创建及运行

Scrapy是一个为了爬取网站数据&#xff0c;提取结构性数据而编写的应用框架。 可以应用在包括数据挖 掘&#xff0c;信息处理或存储历史数据等一系列的程序中。 1. 安装scrapy&#xff1a; pip install scrapy 注意&#xff1a;需要安装在python解释器相同的位置,例如&#xf…...

PyTorch: torch.nn 子模块及其在循环神经网络中的应用

目录 torch.nn子模块详解 nn.utils.rnn.PackedSequence 参数说明 注意事项 示例代码 nn.utils.rnn.pack_padded_sequence 参数说明 返回值 注意事项 示例代码 nn.utils.rnn.pad_packed_sequence 参数说明 返回值 注意事项 示例代码 nn.utils.rnn.pad_sequence …...

【QT】自定义代理类

目录 1 我们为什么要使用自定义代理类&#xff1f; 2 自定义代理类的基本设计要求 3 自定义代理的功能 4 基于QSpinBox的自定义代理类 5 自定义代理类的使用 1 我们为什么要使用自定义代理类&#xff1f; 传统的模型-视图框架可以让我们实现逻辑展示相分离&#xff0c;我们…...

线程休眠、线程让步、线程优先级相关内容学习笔记

1、线程休眠 &#xff08;1&#xff09;sleep() 如果需要让当前正在执行的线程暂停一段时间&#xff0c;并进入阻塞状态&#xff08;Timed_Waiting)&#xff0c;则可以通过调用Thread类的静态sleep()方法来实现。 static void sleep(long millis)&#xff1a;让当前正在执行的线…...

paddle指定运行gpu

在PaddlePaddle中指定使用GPU进行运行非常简单。首先&#xff0c;确保你的机器上已经安装了CUDA和cuDNN&#xff0c;并且正确配置了GPU环境。然后&#xff0c;按照以下步骤进行操作&#xff1a; 导入PaddlePaddle库&#xff1a; import paddle设置使用的设备为GPU&#xff1a…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

React19源码系列之 事件插件系统

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...

6.9-QT模拟计算器

源码: 头文件: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMouseEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);…...

qt+vs Generated File下的moc_和ui_文件丢失导致 error LNK2001

qt 5.9.7 vs2013 qt add-in 2.3.2 起因是添加一个新的控件类&#xff0c;直接把源文件拖进VS的项目里&#xff0c;然后VS卡住十秒&#xff0c;然后编译就报一堆 error LNK2001 一看项目的Generated Files下的moc_和ui_文件丢失了一部分&#xff0c;导致编译的时候找不到了。因…...

Spring事务传播机制有哪些?

导语&#xff1a; Spring事务传播机制是后端面试中的必考知识点&#xff0c;特别容易出现在“项目细节挖掘”阶段。面试官通过它来判断你是否真正理解事务控制的本质与异常传播机制。本文将从实战与源码角度出发&#xff0c;全面剖析Spring事务传播机制&#xff0c;帮助你答得有…...

后端下载限速(redis记录实时并发,bucket4j动态限速)

✅ 使用 Redis 记录 所有用户的实时并发下载数✅ 使用 Bucket4j 实现 全局下载速率限制&#xff08;动态&#xff09;✅ 支持 动态调整限速策略✅ 下载接口安全、稳定、可监控 &#x1f9e9; 整体架构概览 模块功能Redis存储全局并发数和带宽令牌桶状态Bucket4j Redis分布式限…...

LeetCode第244题_最短单词距离II

LeetCode第244题&#xff1a;最短单词距离II 问题描述 设计一个类&#xff0c;接收一个单词数组 wordsDict&#xff0c;并实现一个方法&#xff0c;该方法能够计算两个不同单词在该数组中出现位置的最短距离。 你需要实现一个 WordDistance 类: WordDistance(String[] word…...