当前位置: 首页 > 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…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

Bean 作用域有哪些?如何答出技术深度?

导语&#xff1a; Spring 面试绕不开 Bean 的作用域问题&#xff0c;这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开&#xff0c;结合典型面试题及实战场景&#xff0c;帮你厘清重点&#xff0c;打破模板式回答&#xff0c…...

tomcat指定使用的jdk版本

说明 有时候需要对tomcat配置指定的jdk版本号&#xff0c;此时&#xff0c;我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...

通过MicroSip配置自己的freeswitch服务器进行调试记录

之前用docker安装的freeswitch的&#xff0c;启动是正常的&#xff0c; 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...