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

目录

二级指针

二级指针的用途

多级指针的定义和使用

指针和数组之间的关系

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

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

传参的形式(指针)

数组传参时会退化为指针

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

echarts柱状图加单位,底部文本溢出展示

刚开始设置了半天都不展示单位&#xff0c;后来发现是被挡住了&#xff0c;需要调高top值 // 基于准备好的dom&#xff0c;初始化echarts实例var myChart echarts.init(document.getElementById("echartD"));rankOption {// backgroundColor: #00265f,tooltip: {…

借助 ControlNet 生成艺术二维码 – 基于 Stable Diffusion 的 AI 绘画方案

&#xfeff;背景介绍 在过去的数月中&#xff0c;亚马逊云科技已经推出了多篇 Blog&#xff0c;来介绍如何在亚马逊云科技上部署 Stable Diffusion&#xff0c;或是如何结合 Amazon SageMaker 与 Stable Diffusion 进行模型训练和推理任务。 为了帮助客户快速、安全地在亚马…

解锁前端新潜能:如何使用 Rust 锈化前端工具链

前言 近年来&#xff0c;Rust的受欢迎程度不断上升。首先&#xff0c;在操作系统领域&#xff0c;Rust 已成为 Linux 内核官方认可的开发语言之一&#xff0c;Windows 也宣布将使用 Rust 来重写内核&#xff0c;并重写部分驱动程序。此外&#xff0c;国内手机厂商 Vivo 也宣布…

汉泰克1025G信号发生器二次开发(python和C)

信号发生器&#xff1a;汉泰克1025G SDK开发资料&#xff1a;http://www.hantek.com.cn/products/detail/48 1.python接口 网上已经有大神制作了python的封装接口&#xff1a;https://github.com/AIMAtlanta/Hantek_1025G 这里为了方便查找就再张贴一遍&#xff1a; # -*- c…

升级 Vite 5 出现警告 The CJS build of Vite‘s Node API is deprecated.

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

DOM高级

1.1 自定义属性操作 1.1.1 获取属性值 element.属性 element.getAttribute(属性) 区别&#xff1a; element.属性&#xff1a;获取元素内置属性 element.getAttribute(属性)&#xff1a;获取自定义的属性 1.1.2 设置属性值 element.属性 值 element.setAttribute(属性&a…

多特征变量序列预测(一)——CNN-LSTM风速预测模型

目录 往期精彩内容&#xff1a; 前言 1 多特征变量数据集制作与预处理 1.1 导入数据 1.2 数据集制作与预处理 2 基于Pytorch的CNN-LSTM 预测模型 2.1 定义CNN-LSTM预测模型 2.2 设置参数&#xff0c;训练模型 3 模型评估与可视化 3.1 结果可视化 3.2 模型评估 代码…

11.文件和异常

文件和异常 实际开发中常常会遇到对数据进行持久化操作的场景&#xff0c;而实现数据持久化最直接简单的方式就是将数据保存到文件中。说到“文件”这个词&#xff0c;可能需要先科普一下关于文件系统的知识&#xff0c;但是这里我们并不浪费笔墨介绍这个概念&#xff0c;请大…

柯桥小语种学习,留学韩语 生活日常口语 语法

① N이다/A/V/았ㄹ/을지도 모르다 说不定 이미 도착했을 지도 모르니까 전화해 봐요 说不定已经到了&#xff0c;打电话试试 주말에 세일이 있을지도 모르니까 주말에 가 보자 周末说不定会搞活动&#xff0c;我们周末去吧 ② ㄴ/은/는/았었는/ㄹ/을지 모르다 不知道 처음이…

第四站:C/C++基础-指针

目录 为什么使用指针 函数的值传递&#xff0c;无法通过调用函数&#xff0c;来修改函数的实参 被调用函数需要提供更多的“返回值”给调用函数 减少值传递时带来的额外开销&#xff0c;提高代码执行效率 使用指针前: 使用指针后: 指针的定义: 指针的含义(进阶): 空指针…

4.6 BOUNDARY CHECKS

我们现在扩展了tile矩阵乘法内核&#xff0c;以处理具有任意宽度的矩阵。扩展必须允许内核正确处理宽度不是tile宽度倍数的矩阵。通过更改图4.14中的示例至33 M、N和P矩阵&#xff0c;图4.18创建了矩阵的宽度为3&#xff0c;不是tile宽度&#xff08;2&#xff09;的倍数。图4.…

怎么将营业执照图片转为excel表格?(批量合并识别技巧)

一、为何要将营业执照转为excel表格&#xff1f; 1、方便管理&#xff1a;将营业执照转为excel格式&#xff0c;可以方便地进行管理和整理&#xff0c;快速查找需要的信息。 2、数据处理&#xff1a;Excel可以提供丰富的计算和数据分析功能&#xff0c;转化为excel后方便数据…

【算法设计与分析】网络流

目录 max-flow 和 min-cut流网络 Flow network最小割 Min-cut最大流 Max-flow Greedy algorithmFord–Fulkerson algorithm剩余网络 Residual networkFord–Fulkerson algorithm算法流程 最大流最小割理论 max-flow min-cut theorem容量扩展算法 capacity-scaling algorithm时间…

Rustdesk本地配置文件存在什么地方?

环境&#xff1a; rustdesk1.1.9 Win10 专业版 问题描述&#xff1a; Rustdesk本地配置文件存在什么地方&#xff1f; 解决方案&#xff1a; RustDesk 是一款功能齐全的远程桌面应用。 支持 Windows、macOS、Linux、iOS、Android、Web 等多个平台。 支持 VP8 / VP9 / AV1 …

UDP 和 TCP 、HTTP、HTTPS、SOCKS5协议的不同之处及应用场景

UDP 和 TCP、HTTP、HTTPS、SOCKS5 协议的不同之处及应用场景&#xff1a; UDP (User Datagram Protocol)&#xff1a; 不同之处&#xff1a;UDP 是无连接的&#xff0c;不保证数据包的顺序到达或完整性&#xff0c;也没有流量控制和拥塞控制机制。它尽可能快地将数据包从源主机…

STL标准库与泛型编程(侯捷)笔记4

STL标准库与泛型编程&#xff08;侯捷&#xff09; 本文是学习笔记&#xff0c;仅供个人学习使用。如有侵权&#xff0c;请联系删除。 参考链接 Youbute: 侯捷-STL标准库与泛型编程 B站: 侯捷 - STL Github:STL源码剖析中源码 https://github.com/SilverMaple/STLSourceCo…

面向应用的离线计算系统:周期任务组合策略

1 场景 业务应用系统想大批量利用数据中心的计算能力跑数,回传结果。比如一个个地区的详情数据。而大数据平台通常是调度平台系统,和业务系统是两个独立的平台系统,如何建立交互方式。 业务有个性化的实验策略,需要组合业务条件达到实验效果。比如捞取不同的数据实验算法…

4.8 SUMMARY 4.9 EXERCISES

总之&#xff0c;在现代处理器中&#xff0c;程序的执行速度可能会受到内存速度的严重限制。为了很好地利用CUDA设备的执行吞吐量&#xff0c;应该在内核代码中获得高计算与全局内存访问率。如果获得的比率很低&#xff0c;则内核受内存约束&#xff1b;即其执行速度受从内存访…

鸿蒙Ability开发-Stage模型下Ability的创建和使用

创建Ability和Page页面 创建两个Ability&#xff1a;EntryAbility&#xff0c;DetailsAbility&#xff0c;其中EntryAbility是由工程默认创建的&#xff0c;这里我们只讲如何创建DetailsAbility。 使用DevEco Studio&#xff0c;选中对应的模块&#xff0c;单击鼠标右键&…

IDEA+Git——项目分支管理

IDEAGit——项目分支管理 1. 前言2. 基础知识点2.1. 分支区分2.2. Git 代码提交规范2.3. 四个工作区域2.4. 文件的四种状态2.5. 常用命令2.6 注重点 3. IDEA分支管理 1. 前言 在Git中&#xff0c;分支是项目的不同版本&#xff0c;当开始开发一个新项目时&#xff0c;主分支通常…