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

目录

二级指针

二级指针的用途

多级指针的定义和使用

指针和数组之间的关系

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

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

传参的形式(指针)

数组传参时会退化为指针

 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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/607911.shtml

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

相关文章

别闹了,真的不是你的技术菜!!!

最近经常听到有小伙伴总是在抱怨自己的技术菜&#xff0c;公司没有机会让自己去成长技术&#xff0c;于是小编就此场景来写一篇文章&#xff0c;希望对大家有帮助。 错误的理解CRUD工程师 CRUD工程师这个名称是很多小伙伴都听过的&#xff0c;并且很多工程师都把自己比作是代…

Ubuntu 18.04.5 LTS 解决安装包复杂依赖相关问题解决的主要法则和VIM的安装实录

前言&#xff1a;目标和环境 环境&#xff1a; Ubuntu 18.04.5 LTSVMware 目标&#xff1a; 安装vim&#xff0c;解决包依赖的冲突&#xff1a; 本文&#xff0c;通过一个很好的实例&#xff0c;诠释了&#xff0c;LINUX系统下&#xff0c;安装一个应用遇到的依赖库问题如何…

Wilcoxon秩和检验-校正P值(自备)

R语言 boxplot作图 图内展示校正后的P值(padj)_r语言 p值校正-CSDN博客 FDR错误发现率-P值校正学习_fdr和p值的关系-CSDN博客 原理介绍&#xff1a; Benjamini-Hochberg 方法介绍 有N次假设检验&#xff0c;对每一次假设检验都计算其P值&#xff0c;然后将计算出的P值按照…

对接讯飞聊天机器人接口--复盘

1、准备工作 1&#xff09;、进入以下平台进行注册&#xff0c;登录后&#xff0c;点击红框处 2&#xff09;、点击个人免费包&#xff08;会弹出实名认证&#xff0c;先进行实名认证&#xff09; 3&#xff09;、认证后&#xff0c;会进入以下界面&#xff0c;先添加应用 4&am…

代码随想录刷题题Day29

刷题的第二十九天&#xff0c;希望自己能够不断坚持下去&#xff0c;迎来蜕变。&#x1f600;&#x1f600;&#x1f600; 刷题语言&#xff1a;C Day29 任务 ● 01背包问题&#xff0c;你该了解这些&#xff01; ● 01背包问题&#xff0c;你该了解这些&#xff01; 滚动数组 …

软件测试|深入理解Python的encode()和decode()方法

简介 在Python中&#xff0c;字符串是不可变的序列对象&#xff0c;它由Unicode字符组成。当我们需要在字符串和字节之间进行转换时&#xff0c;Python提供了两个非常重要的方法&#xff1a;encode()和decode()。这两个方法允许我们在Unicode字符和字节之间进行相互转换&#…

认知能力测验,⑤破解图形推理测试题,校招社招网申在线测评必用

认知能力测试&#xff0c;如今是每个求职者必须要面对的&#xff0c;有的人可以顺顺利利通过&#xff0c;而有的人只能够遗憾止步。想要通过认知能力测验&#xff0c;并不是一件易事&#xff0c;而今天要说的图形推理&#xff0c;仅仅是其中的一个部分&#xff0c;抛砖引玉&…

【数据分析实战】冰雪大世界携程景区评价信息情感分析采集词云

文章目录 引言数据采集数据集展示数据预处理 数据分析评价总体情况分析本人浅薄分析 各游客人群占比分析本人浅薄分析 各评分雷达图本人浅薄分析 差评词云-可视化本人浅薄分析 好评词云-可视化本人浅薄分析 综合分析写在最后 今年冬天&#xff0c;哈尔滨冰雪旅游"杀疯了&q…

IP地址的网络安全防护和预防

网络安全对于保护个人和组织的信息资产至关重要&#xff0c;而IP地址是网络通信的基础。在这篇文章中&#xff0c;IP数据云将探讨IP地址的网络安全防护和预防措施&#xff0c;以确保网络的安全性和可靠性。 IP地址是互联网上每个设备在网络中的唯一标识符。有IPv4和IPv6两种类…

docker部署awvs

docker部署awvs cantos部署docker点这里 下载镜像 docker pull xiaomimi8/awvs14-log4j-2022 docker images 查看本地所有镜像启动镜像 docker run -it -d&#xff08;后台运行&#xff09; -p&#xff08;端口映射&#xff09; 13443&#xff08;主机端口&#xff09;:3443&…

【Databand】日期时间函数

文章目录 获取当前日期和时间日期格式化函数日期加减运算日期时间和时间戳转化日期时间各部分拆分日期时间加减运算实际应用扩展总结 获取当前日期和时间 Databend 使用 UTC 作为默认时区&#xff0c;并允许您将时区更改为当前地理位置。 -- 查看时区 select timezone(); ---…

【Java开发工具】windows和mac多版本JDK 安装指南:让你在开发中轻松应对不同版本需求

解决思路 通过动态修改JDK环境变量中的JAVA_HOME 将值改成相应的JDK安装目录&#xff0c;来达到在同一电脑中安装不同版本jdk的效果。 windows系统 安装的jdk目录 右键→属性→高级系统设置→高级→环境变量→系统变量。 新建4个系统变 量变量值,变量名分别为&#xff1a…

statsmodels.tsa 笔记 detrend(去趋势)

1 基本使用方法 statsmodels.tsa.tsatools.detrend(x, order1, axis0) 2 参数说明 x数据。如果是二维数组&#xff0c;那么每一行或每一列将独立地去除趋势&#xff0c;但趋势的阶数是一样的。order趋势的多项式阶数。0 表示常数趋势&#xff08;即没有趋势&#xff09;&…

kubernetes Adminssion Webhook 准入控制器 (ImagePolicyWebhook)

开头语 写在前面&#xff1a;如有问题&#xff0c;以你为准&#xff0c; 目前24年应届生&#xff0c;各位大佬轻喷&#xff0c;部分资料与图片来自网络 介绍 原理 流程 Admission Webhook 准入控制器Vebhook是准入控制插件的一种&#xff0c;用于拦截所有向APISERVER发送的…

超声波清洗机是智商税吗?哪些超声波清洗机值得买?这些值得入手

自打超声波清洗机问世以来&#xff0c;就有非常多朋友会有这个疑问&#xff01;超声波清洗机到底是不是智商税呢&#xff1f;其实不光是大家&#xff0c;一开始我也有这个疑问&#xff0c;但是通过了解了超声波清洗机的一些工作原理之后&#xff0c;会发现&#xff0c;其实超声…

服务器组网方案

在当今数字化时代&#xff0c;服务器组网方案不仅是企业信息管理的关键&#xff0c;更是支撑业务运作的核心架构 。为了实现高效的数据处理和存储&#xff0c;服务器组网方案成为企业不可或缺的一部分。本文将深入探 讨服务器组网方案的核心要素和实施策略&#xff0c;明确其在…

RTMP vs SRT:延迟与最大带宽的比较

引言 文来自Haivision的白皮书&#xff0c;比较了RTMP和SRT两种流媒体协议的优缺点&#xff0c;并通过实验测试了两种协议在延迟和最大带宽两方面的表现。 本文福利&#xff0c; 免费领取C音视频学习资料包学习路线大纲、技术视频/代码&#xff0c;内容包括&#xff08;音视频…

zabbix监控部署

目录 一、什么是zabbix&#xff1f; 二、zabbix监控原理 三、zabbix常见的五个程序 四、zabbix监控mysql实验 1、部署服务端 2、部署客户端 3、自定义监控内容 一、什么是zabbix&#xff1f; zabbix 是一个基于 Web 界面的提供分布式系统监视以及网络监视功能的企业级的…

如何让GPT支持中文

上一篇已经讲解了如何构建自己的私人GPT&#xff0c;这一篇主要讲如何让GPT支持中文。 privateGPT 本地部署目前只支持基于llama.cpp 的 gguf格式模型&#xff0c;GGUF 是 llama.cpp 团队于 2023 年 8 月 21 日推出的一种新格式。它是 GGML 的替代品&#xff0c;llama.cpp 不再…

构建异步高并发服务器:Netty与Spring Boot的完美结合

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 ChatGPT体验地址 文章目录 前言IONetty1. 引入依赖2. 服务端4. 客户端结果 总结引导类-Bootstarp和ServerBootstrap连接-NioSocketChannel事件组-EventLoopGroup和NioEventLoopGroup 送书…