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

news2024/10/5 14:25:49

目录

二级指针

二级指针的用途

多级指针的定义和使用

指针和数组之间的关系

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

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

传参的形式(指针)

数组传参时会退化为指针

 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.coloradmin.cn/o/1368037.html

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

相关文章

JavaScript的闭包、执行上下文、到底是怎么回事?还有必要学吗?

在上一课&#xff0c;我们了解了 JavaScript 执行中最粗粒度的任务&#xff1a;传给引擎执行的代码段。并且&#xff0c;我们还根据“由 JavaScript 引擎发起”还是“由宿主发起”&#xff0c;分成了宏观任务和微观任务&#xff0c;接下来我们继续去看一看更细的执行粒度。 一…

数据结构与算法-栈-移掉K位数字

移掉K位数字 给你一个以字符串表示的非负整数 num 和一个整数 k &#xff0c;移除这个数中的 k **位数字&#xff0c;使得剩下的数字最小。请你以字符串形式返回这个最小的数字。 示例 1 &#xff1a; 输入&#xff1a;num "1432219", k 3 输出&#xff1a;&quo…

葡萄酒的独特国度智利

葡萄酒的独特国度智利 一、独特的智利葡萄酒 智利葡萄酒是在20世纪90年代以后才逐渐走向了世界&#xff0c;由于低税、口味独特等优点&#xff0c;深受大众喜爱。因为智利独待的气候其生产的葡萄别有风味&#xff0c;为其产出优质葡萄酒奠定了基础&#xff0c;再加上欧洲古老的…

电口模块SFP-GE-T:实现光口与电口之间的转换

电口模块是一种用于实现光口转电口功能的设备&#xff0c;在网络通信中起到重要作用。电口模块没有光电转换的过程&#xff0c;只是传输电信号。本文介绍电口模块的作用、分类、以及使用方法。 一、什么是电口模块 电口模块又被称为光转电模块&#xff0c;它是一种支持热插拔…

vue3+echarts应用——深度遍历html的dom结构并用树图进行可视化

文章目录 ⭐前言&#x1f496;vue3系列文章 ⭐html数据解析&#x1f496; html字符串转为html对象&#x1f496; 深度遍历html对象内容 ⭐echarts 树图的渲染&#x1f496; 处理html内容为树状结构&#x1f496; 渲染树状图&#x1f496; inscode代码块 ⭐总结⭐结束 ⭐前言 大…

程序员必知!备忘录模式的实战应用与案例分析

备忘录模式允许在不破坏封装性下捕获并在外部保存对象状态&#xff0c;支持状态恢复&#xff0c;常用于撤销、历史记录等功能。例如在线文档编辑器的撤销操作&#xff0c;编辑器作为原发起人记录状态并提供保存与恢复方法&#xff0c;历史记录或撤销为管理者&#xff0c;保存备…

Vue3打印插件Print.js的使用

文章目录 一、Print.js二、安装2.1、 js文件2.2、npm2.3、CDN 三、使用3.1、网页&#xff08;HTML&#xff09;打印3.2、PDF 打印3.3、图像打印3.4、JSON 打印 四、最后 一、Print.js 在使用 Print.js 插件之前&#xff0c;我们可以通过下面的链接先了解和认识一下这个 JavaScr…

Linux下Redis6下载、安装和配置教程-2024年1月5日

Linux下Redis6下载、安装和配置教程-2024年1月5日 一、下载二、安装三、启动四、设置开机自启五、Redis的客户端1.Redis命令行客户端2.windows上的图形化桌面客户端 一、下载 1.Redis的官方下载&#xff1a;https://redis.io/download/ 2.网盘下载&#xff1a; 链接&#xff…

基于FPGA的万兆以太网学习(1)

万兆(10G) 以太网测速视频:FPGA 实现UDP万兆以太网的速度测试 1 代码结构 2 硬件需求 SFP+屏蔽笼可以插入千兆或万兆光模块。SFP+信号定义与 SFP 一致。 3 Xilinx IP 10 Gigabit Ethernet Subsystem IP说明 文章链接: Xilinx IP 10 Gigabit Ethernet Subsystem IP 4 E…

【ZooKeeper高手实战】ZooKeeper常用命令及客户端工具Curator核心功能

&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308; 欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术 的推送 发送 资料 可领取 深入理…

答疑解惑:核技术利用辐射安全与防护考核

前言 最近通过了《核技术利用辐射安全与防护考核》&#xff0c;顺利拿到了合格证。这是从事与辐射相关行业所需要的一个基本证书&#xff0c;考试并不难&#xff0c;在此写篇博客记录一下主要的知识点。 需要这个证书的行业常见的有医疗方面的&#xff0c;如放疗&#xff0c;…

寒假护眼台灯哪款更好?五款高品质护眼台灯推荐

近年来学生近视的现象越来越严重了&#xff0c;而且近视的年龄也越来越小了&#xff0c;不少还没开始上小学的孩子&#xff0c;就已经戴上了厚厚的近视眼镜。而那些高年级的学生更是近视的重灾区&#xff0c;不仅每天需要高强度的学习和长时间用眼&#xff0c;甚至晚上都还需要…

Java内存模型(JMM)是基于多线程的吗

Java内存模型&#xff08;JMM&#xff09;是基于多线程的吗 这个问题按我的思路转换了下&#xff0c;其实就是在问&#xff1a;为什么需要Java内存模型 总结起来可以由几个角度来看待「可见性」、「有序性」和「原子性」 面试官&#xff1a;今天想跟你聊聊Java内存模型&#…

【pdf密码】pdf打印密码强制解除

正常的PDF文件是可以打印的&#xff0c;如果PDF文件打开之后发现文件不能打印&#xff0c;我们需要先查看一下自己的打印机是否能够正常运行&#xff0c;如果打印机是正常的&#xff0c;我们再查看一下&#xff0c;文件中的打印功能按钮是否是灰色的状态。 如果PDF中的大多数功…

LED电平显示驱动电路图

LB1409九位LED电平显示驱动电路 如图所示为LBl409九位LED电平显示驱动电路。图&#xff08;a&#xff09;是用LB1409做电平显示驱动电路&#xff0c;图&#xff08;b&#xff09;是应用基准电压电平显示驱动电路。LB1409是日本东京互洋电机株式会社生产的产品&#xff0c;与其…

最新ThinkPHP版本实现证书查询系统,实现批量数据导入,自动生成电子证书

前提&#xff1a;朋友弄了一个培训机构&#xff0c;培训考试合格后&#xff0c;给发证书&#xff0c;需要一个证书查询系统。委托我给弄一个&#xff0c;花了几个晚上给写的证书查询系统。 实现功能&#xff1a; 前端按照姓名手机号码进行证书查询证书信息展示证书展示&#x…

0108作业

#include "widget.h"Widget::Widget(QWidget *parent): QWidget(parent) {this->setWindowTitle("腾讯会议");this->resize(470,800);//设置界面大小this->setFixedSize(470,800);//锁定界面大小this->setStyleSheet("background-color:w…

老板向我请教Transformer的原理,我没讲清

最近&#xff0c;一直跟别人讲大语言模型带来的AIGC是巨变&#xff0c;涉及了多个领域&#xff0c;并且谈了我们工作和生活中可以利用的地方&#xff0c;以及预测2024年大语言模型将在哪些领域爆发。这时&#xff0c;老板过来了&#xff0c;就聊&#xff0c;问&#xff0c;谈到…

解决:Unity : Error while downloading Asset Bundle: Couldn‘t move cache data 问题

目录 问题&#xff1a; 尝试 问题得到解决 我的解释 问题&#xff1a; 最近游戏要上线&#xff0c;发现一个现象&#xff0c;部分机型在启动的时候闪退或者黑屏&#xff0c;概率是5%左右&#xff0c;通过Bugly只有个别机型才有这个现象&#xff0c;其实真实情况比这严重的多…

【操作系统】复习汇总(各章节知识图谱)

第1章&#xff1a; 第2章&#xff1a; 第3章&#xff1a; 第4章&#xff1a; 第5章&#xff1a; 第6章&#xff1a; 第7章&#xff1a; 第8章&#xff1a; 第9章&#xff1a;