【类型黑市】指针

news2024/9/21 0:12:25

大家好我是#Y清墨,今天我要介绍的是指针。

意义

指针就是存放内存地址的变量。

分类

因为变量本身是分类型的,我们学过的变量类型有 int, long long, char, double, string, 甚至还有结构体变量。

同样,指针也分类型,如果指针指向一个存放 int 数据的地址,那么这个指针就是 int 类型的指针;如果指针指向一个存放 long long 数据的地址,那么这个指针就是 long long 类型指针,.....,如果一个指针是指向 stu 结构体数据的地址,那么这个指针就是 stu 结构体类型的指针。

运算

每一种数据类型,都有一些与之匹配的运算。例如,对于 int 类型,有 +, -, *, /, % 运算。如果对应 string 类型,+ 运算虽然也有,但是那就不是数学上的加法,而是对两个字符串进行连接操作,而且,string 类型也没有 -, *, / 运算。

同理,指针运算也有一些与之对应的运算。这是我们下一步要学习的重点。

定义指针

定义指针的时候,需要用到 星号(*),我们直接通过一些例子来演示:

int *p1; // 定义一个 int 指针,名字叫 p1,它将来可以用来存放一个 int 变量的内存地址
double *p2; // 定义一个 double 指针,名字叫 p2,它将来可以用来存放一个 double 变量的内存地址
long long *p3; // 定义一个 long long 指针,名字叫 p3,它将来可以用来存放一个 long long 变量的内存地址
struct stu
{
    string name;
    int yu,shu,ying;
};
stu *p4; // 定义一个 stu 结构体 指针,名字叫 p4,它将来可以用来存放一个 stu 结构体变量的内存地址

指针的赋值

指针变量的赋值和别的变量的赋值是一样的,只不过赋的值是地址信息。地址的分配是由操作系统来做的,我们的程序是和操作系统配合,我们的程序让操作系统给我们分配地址,操作系统把分配到的内存的地址告诉我们的程序,我们再来用这片内存。(所以,这里有很多很多啰嗦事,我们只能挑一些很简单的来讲)

我们要学的其实是如何获取一个变量的地址。获取变量地址需要用 & 运算符。

int a=5;
int *p1;
p1 = &a; // 获取变量 a 的地址,把地址赋给 p1

int b;
int *p2 = &a; // 定义指针变量 p2 的时候,顺带赋值

上面对 p1 和 p2 的赋值,一个地方是有 星号 的,一个地方是没有星号的,要特别注意。

对 p1 赋值,前面不能有星号;但是,如果 p1 是在声明的时候顺带赋值,声明的时候需要有星号。

指针的间接引用

我们已经知道,指针是指向一个变量的内存地址。既然如此,那我们能否通过指针去修改这个变量的值呢?

答案是可以的。这就是指针的间接引用。

举个例子,int 指针 p1 指向 123450 这个地址的内存,123450 就是“门牌号”(这个地址是我随便说的),我现在要对 123450 这个地址的 int 变量赋值,赋值成 99,这个过程就是对指针 p1 的间接引用。因为,我引用的不是指针的值(指针的值是地址),我引用的是指针的值对应的地址的值。

太绕了,直接用例子说明:

#include<bits/stdc++.h>
using namespace std;
int main()
{

	int a[1000]; //定义了数组 a
	
	*(a+5)=99; // a+5 相当于一个指针,指向 a[5] ,所以这一句相当于 a[5] = 99
	
	printf("a[5]=%d\n",a[5]);
	int b=0;
	
	int *p = &b;
	
	*p = 71; // 因为 p 指向 b 对应的内存,这一句相当于 b=71

	printf("b=%d\n",b);

	return 0;
}

但是,实际上没有人这样写程序的。如果我要对 a[5] 赋值,为什么不直接写 a[5]=99呢?

所以,上面的例子,只是为了讲述语法,讲述原理,帮助大家理解

市选拔赛是要考指针的,我敢肯定,不会考太复杂的。我们要学到这个程度:

  1. 会定义指针变量
  2. 会给指针变量赋值
  3. 会取一个变量的内存地址(&运算)
  4. 会间接引用(*运算)

区分 * 和 & 运算符

我们过去一直都认为 * 就是乘法运算符,怎么今天就说他是地址的间接引用了呢?我们怎么区分,什么情况把它理解成乘法运算,什么情况把它理解成指针运算?

乘法运算

乘法运算的 * 是双目运算符。意思是,* 的前面和后面都有一个参数(数字),例如,5*2 ,或者 12*13,可以看到 * 是加在两个数字中间的。

 地址访问运算符

地址访问运算符是单目运算符,* 前面是没有参数的,后面有参数,而且后面的参数是指针类型。

#include<bits/stdc++.h>
using namespace std;
int main()
{

	int a=100,b=500;
	int *p1,*p2; //这里是定义语句,这里的 * 并不是"地址访问运算符"
	p1 = &a;
	p2 = &b;
	
	if(*p1 > * p2) cout<<"p1 指向的数字比 p2 指向的数字大"<<endl;
	else cout<<"p1 指向的数字小于等于 p2 指向的数字"<<endl;

	int c = *p1 * *p2;
	printf("c=%d\n",c);

	return 0;
}

乘法运算的优先级低于地址访问,所以,是访问地址返回数值,然后再来乘。

类似,& 既可以是按位与运算符,也可以是取地址运算符,字符的样子是一样的,区分方法也是通过单目运算符和双目运算符进行区分。 按位与是双目运算符,前后应该有两个整数;取地址运算符是单目运算符,前面没有参数的。

二维数组和指针

先复习一下一维数组和指针的关系

int x[10];
int * p;
p = x; // 直接引用 x 的时候,相当于获取了 x[0] 的地址,本行等同于 p = &x[0]
cout<<*(p+2) * *(p+3); // 本行其实就是输出 x[2]*x[3]

那二维数组的情况又是怎么样呢?

以前我们学习二维数组的时候就说过,二维数组相当于是一维数组的数组,例如,我们定义 int x[4][3],相当于说有一个数组,有 4 个元素,而每个元素又是一个一维数组,里面的这个一维数组是包含了 3 个 int 变量。这句话很绕,但是很关键,如果这句话理解不了,下面的内容也理解不了的。

所以,假设我们定义了 int x[4][3] 之后,如果引用 x[0] ,相当于引用的是第一个一维数组,这个一维数组包含了 { x[0][0], x[0][1], x[0][2] },x[1] 相当于引用第二个一维数组 { x[1][0], x[1][1], x[1][2] },x[2] 是第三个一维数组 { x[2][0], x[2][1], x[2][2] },x[3] 是第四个一维数组 { x[3][0], x[3][1], x[3][2] }。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int x[4][3];

	for(int i=0;i<4;i++)
	printf("x[%d]=%d\n",i,x[i]);
	
	return 0;
}

程序的运行结果是:

一个 int 是 4 个 byte,3 个 int 就是 12 个 Byte,所以看到 x[0] 和 x[1] 之间差了 12 ,单位就是 Byte。

所以,x[i] 其实是一个指针,它存放的是内容是内存地址。x[0] 是地址,x[1] 是地址,x[2], x[3] 都是地址。

#include<bits/stdc++.h>
using namespace std;
int *p[4];
int main()
{
	int x[4][3]={1,2,3,4,5,6,7,8,9,10,11,12};
	
	for(int i=0;i<4;i++)
		p[i] = x[i];

	for(int i=0;i<4;i++)
		printf("p[%d]的地址是=%d p[%d]=%d\n",i,&p[i],i,p[i]);

	
	for(int i=0;i<4;i++)
	{
		for(int j=0;j<3;j++)
			printf("x[%d][%d] 的内存地址是 %d\n",i,j,&x[i][j]);
		
		printf("\n");
	}

	return 0;
}

结合输出信息,p 数组有 4 个元素,p[0] 这个数据是存放在地址为 4878400 的内存那里,值是 228720080。而这个值本身也是一个内存地址,p[1], p[2], p[3] 的情况都类似,它们的内容(值)都是地址信息,。

说得更具体一点,p[0] 的值就是 x[0][0] 的地址,p[1] 的值就是 x[1][0] 的地址,p[2] 就是 x[2][0] 的地址.......

之前我们已经学习过,基于地址偏移的方法去间接引用数据,那么对于二维数组,如何进行间接引用呢?

既然 p[0] 存放的是 x[0][0] 的地址,x 数组的元素又是连续排列的,所以,p 就是 x[0][0] 的地址,因此 *(p[0]+1) 就是对 x[0][0] 的间接引用。而 p[1] 存放的是 x[1][0] 的地址,因此 p[1]+2 就是 x[1][2] 的地址,*(p[1]+2) 就是 x[1][2] 的间接引用.....,这里,用 p 来做间接引用也行,用 x 做间接引用也行,p[1] 的值就是 x[1],所以,*(p[1]+2) 相当于 *(x[1]+2),也相当于 *(*(x+1)+2)

#include<bits/stdc++.h>
using namespace std;
int *p[4];
int main()
{
	int x[4][3]={1,2,3,4,5,6,7,8,9,10,11,12};
	
	for(int i=0;i<4;i++)
		p[i] = x[i];

	// 通过 p 指针间接引用	
	for(int i=0;i<4;i++)
	{
		for(int j=0;j<3;j++)
			printf("x[%d][%d]=%d ",i,j,*(p[i]+j));
		
		printf("\n");
	}

	printf("\n");
	// 通过 x 指针间接引用	
	for(int i=0;i<4;i++)
	{
		for(int j=0;j<3;j++)
			printf("x[%d][%d]=%d ",i,j,*(*(x+i)+j));
		
		printf("\n");
	}

	return 0;
}

上面的代码是比较绕的,死机硬背的话过上一阵就忘了,需要理解再来记忆。

首先说,x 是二维数组,也就是说它是一维数组的数组。x+i 相当于 x[i] , x+1 和 x+2 之间差了 12 的(因为内层的一维数组有 3 个 int,每个 int 是 4 个 byte,3*4=12)。x[i] 就是记录着第 i 个一维数组的起始地址,x[i] (等同于 x+i ) 相当于就是取到 x[i][0] 的地址 ,然后基于 这个地址再偏移 j 个 int,就是 x[i][j] 的地址了。

*(*(x+i)+j)) 这个表达式里有两个 * 运算符,右边的运算符,就是取到 x[i][0] 的地址,或者说是第 i 个一维数组的开始地址(i 从 0 开始算),所以,左边的 * 运算之后之后,得到的还是地址。然后左边的 * 就是基于地址去间接引用 x[i][j] 了。

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

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

相关文章

云韧性,现代云服务不可或缺的组成部分

韧性&#xff0c;一个物理学概念&#xff0c;表示材料在变形或者破裂过程中吸收能量的能力。韧性越好&#xff0c;则发生脆性断裂的可能性越小。 如今&#xff0c;韧性也延伸到企业特质、产品特征等之中&#xff0c;用于形容企业、产品乃至服务的优劣。同样&#xff0c;随着云…

3. Internet 协议的安全性

3. Internet 协议的安全性 (1) 常用网络协议的功能、使用的端口及安全性 HTTP协议 功能:用于从服务器传输超文本到本地浏览器。端口:默认是80端口。安全性:不提供数据加密,存在数据泄露和中间人攻击风险。使用HTTPS协议(443端口)可以增强安全性。FTP协议 功能:实现文件的…

电脑录课软件哪个好用,提高教学效率?电脑微课录屏软件推荐

在当今这个数字化时代&#xff0c;教育领域也迎来了翻天覆地的变化。随着远程教学和在线学习的普及&#xff0c;教师们开始寻求更高效、更便捷的教学工具来提升教学质量和学生的学习体验。电脑录课软件&#xff0c;作为现代教育技术的重要组成部分&#xff0c;能够帮助教师轻松…

【CPP】类与继承

14 类与继承 在前面我们提到过继承的一些概念,现在我们来回顾一下 打个比方:在CS2中我们把玩家定义为一个类 class 玩家: 血量:100阵营(未分配)服饰(未分配)位置(未分配)武器(未分配)是否允许携带C4(未分配)是否拥有C4(未分配) 当对局创建时,会新生成两个类,这两个类继承自&qu…

【Linux庖丁解牛】—Linux基本指令(上)!

&#x1f308;个人主页&#xff1a;秋风起&#xff0c;再归来~&#x1f525;系列专栏&#xff1a; Linux庖丁解牛 &#x1f516;克心守己&#xff0c;律己则安 目录 1、 pwd命令 2、ls 指令 3、cd 指令 4、Linux下的根目录 5、touch指令 6、 stat指令 7、mkdi…

LabVIEW提高开发效率技巧----采用并行任务提高性能

在复杂的LabVIEW开发项目中&#xff0c;合理利用并行任务可以显著提高系统的整体性能和响应速度。并行编程是一种强大的技术手段&#xff0c;尤其适用于实时控制、数据采集以及多任务处理等场景。LabVIEW的数据流编程模型天然支持并行任务的执行&#xff0c;结合多核处理器的硬…

OrCAD使用,快捷键,全选更改封装,导出PCB网表

1 模块名称 2 快捷键使用 H: 镜像水平 V&#xff1a;镜像垂直 R: 旋转 I: 放大 O&#xff1a; 放小 P&#xff1a;放置元器件 W&#xff1a; 步线 B&#xff1a; 总线&#xff08;无电气属性&#xff09; E: 总线连接符&#xff08;和BUS一起用&#xff09…

【网络通信基础与实践第四讲】用户数据报协议UDP和传输控制协议TCP

一、UDP的主要特点 1、UDP是无连接的&#xff0c;减少了开销和发送数据之前的时延 2、UDP使用尽最大努力交付&#xff0c;但是不保证可靠交付 3、UDP是面向报文的。从应用层到运输层再到IP层都只是添加一个相应的首部即可 4、UDP没有拥塞机制&#xff0c;源主机以恒定的速率…

基于JAVA+SpringBoot+Vue的学生干部管理系统

基于JAVASpringBootVue的学生干部管理系统 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末附源码下载链接&#x1f345; 哈…

力扣题解2376

大家好&#xff0c;欢迎来到无限大的频道。 今日继续给大家带来力扣题解。 题目描述&#xff08;困难&#xff09;&#xff1a; 统计特殊整数 如果一个正整数每一个数位都是 互不相同 的&#xff0c;我们称它是 特殊整数 。 给你一个 正 整数 n &#xff0c;请你返回区间 …

【Python报错已解决】SyntaxError invalid syntax

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

锐尔15注册机 锐尔文档扫描影像处理系统15功能介绍

锐尔文档扫描影像处理系统是一款全中文操作界面的文件、档案扫描及影像优化处理软件&#xff0c;是目前国内档案数字化行业里专业且优秀的影像优化处理软件。 无论是从纸质文件制作高质量的影像文件&#xff0c;或是检查已经制作好的影像文件&#xff0c;锐尔文档扫描影像处理…

Generative Models from the perspective of Continual Learning【小白读论文】

摘要&#xff1a; 本文在持续学习情况下评估各种生成模型。 本文研究了几种模型如何学习和遗忘&#xff0c;并考虑了各种持续学习策略&#xff1a;回放、正则化、生成重放和微调。 我们使用两个定量指标来估计生成质量和记忆能力。 我们在三个常用的持续学习基准&#xff08;MN…

RabbitMQ08_保证消息可靠性

保证消息可靠性 一、生产者可靠性1、生产者重连机制&#xff08;防止网络波动&#xff09;2、生产者确认机制Publisher Return 确认机制Publisher Confirm 确认机制 二、MQ 可靠性1、数据持久化交换机、队列持久化消息持久化 2、Lazy Queue 惰性队列 三、消费者可靠性1、消费者…

新媒体运营

一、新媒体运营的概念 1.新媒体 2.新媒体运营的五大方向 用户运营 产品运营 。。。 二、新媒体的岗位职责及要求 三、新媒体平台

【redis-01】redis基本数据类型和使用场景

redis系列整体栏目 内容链接地址【一】redis基本数据类型和使用场景https://zhenghuisheng.blog.csdn.net/article/details/142406325 redis基本数据类型和使用场景 一&#xff0c;redis基本数据类型和使用场景1&#xff0c;String数据类型2&#xff0c;Hash数据类型3&#xff…

嵌入式linux系统中rk3588芯片引脚基本操作

第一:开发板中linux系统对应设备节点 进入用户 LED 设备文件夹: 1cd /sys/class/leds/usr_led该目录下的文件分别为 brightness、device、max_brightness、power、subsystem、trigger 和 uevent,需要注意的是 brightness、max_brightness 以及 trigger 文件,这三个文件都是…

共享单车轨迹数据分析:以厦门市共享单车数据为例(五)

先来聊聊啥是共享单车电子围栏&#xff1f; 共享单车电子围栏是一种基于地理位置技术的虚拟边界&#xff0c;用于管理和规范共享单车的停放和使用。这种技术通过在地图上划定特定区域&#xff0c;帮助用户了解哪些地方可以停车&#xff0c;哪些地方不能停车&#xff0c;从而减…

网关登录校验(2)----网关如何将用户信息传递给微服务

1.微服务获取用户信息 现在&#xff0c;网关已经可以完成登录校验并获取登录用户身份信息。但是当网关将请求转发到微服务时&#xff0c;微服务又该如何获取用户身份呢&#xff1f; 由于网关发送请求到微服务依然采用的是Http请求&#xff0c;因此我们可以将用户信息以请求头…

【数据结构】假设二叉树采用二叉链表存储,编写一棵二又树中序遍历的非递归算法。

编程题: 假设二叉树采用二叉链表存储,编写一棵二又树中序遍历的非递归算法。 分析: 算法描述: 非递归中序遍历二叉树的算法使用栈来辅助实现。首先,从根节点开始,沿着左子树不断向下, 将每个节点压入栈中。当到达最左端节点后,开始出栈并访问节点,接着转向右子树,重…