【类型黑市】指针

大家好我是#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 的地址,把地址赋给 p1int 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] = 99printf("a[5]=%d\n",a[5]);int b=0;int *p = &b;*p = 71; // 因为 p 指向 b 对应的内存,这一句相当于 b=71printf("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.xdnf.cn/news/146042.html

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

相关文章

完美转发、C++11中与线程相关的std::ref

目录 模板中的万能引用 std::forward实现完美转发 C11中与线程相关的std::ref 线程函数参数 用函数指针作为线程函数 用lambda表达式作为线程函数 模板中的万能引用 void Func(int& x) {cout << "左值引用" << endl; } void Func(int&&am…

3. Internet 协议的安全性

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

IPv6(四)

文章目录 Path MTUIPv6配置 Path MTU IPv4 对于数据过大的数据包会执行切片操作&#xff0c;但是切片有可能会造成设备性能的降低 IPv6使用Path MTU来传递数据过大的数据包 依次会协商最小的 MTU 单元为了减少中间转发设备的压力&#xff0c;中间转发设备不对 IPv6 报文进行分片…

re题(36)BUUCTF-[WUSTCTF2020]Cr0ssfun

BUUCTF在线评测 (buuoj.cn) 查一下壳&#xff0c;64位elf文件 ctrle找到main()函数 只进行了一个比较函数&#xff0c;看一下check() 猜测是a1中存放的flag&#xff0c;往下继续查看函数 把a1中存的数据都给出来了 写个脚本&#xff0c;输出一下a1&#xff0c;直接就是我们要的…

Python 找到给定点集的简单闭合路径(Find Simple Closed Path for a given set of points)

给定一组点&#xff0c;将这些点连接起来而不相交 例子&#xff1a; 输入&#xff1a;points[] {(0, 3), (1, 1), (2, 2), (4, 4), (0, 0), (1, 2), (3, 1}, {3, 3}}; 输出&#xff1a;按以下顺序连接点将 不造成任何交叉 {(0, 0), (3, …

CSS - 通用左边图片,右边内容,并且控制长度溢出处理模板(vue | uniapp | 微信小程序)

前言 通用模板&#xff0c;可适用于任意前端项目。 如下图所示&#xff0c;手机电脑通用。 示例代码 根据自己的需求修改即可。 <body><div class"container"><!-- 头像图片 --><img class"avatar" src"https://cdn.uviewui.com…

OpenSSH从7.4升级到9.8的过程 亲测--图文详解

一、下载软件 下载openssh 下载地址&#xff1a; Downloads | Library 下载openssl Index of /pub/OpenBSD/OpenSSH/ zlib Home Site 安装的 openssl-3.3.1.tar.gz ,安装3.3.2有问题 安装有问题&#xff0c; 二、安装依赖 yum install -y perl-CPAN perl-ExtUtils-CB…

手动部署并测试内网穿透

文章目录 手动部署并测试内网穿透1、原理2、下载 frp 文件3、配置对应的配置文件4、启动 frp 服务5、效果 手动部署并测试内网穿透 1、原理 原理就是让你需要访问的内网可以被其他内网访问到。 其实就是让内网经过一个公网服务器的转发&#xff0c;使得能够被访问。 这里我们需…

【Python报错已解决】ModuleNotFoundError: No module named ‘tensorflow‘

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

物联网开发+充电桩管理系统+充电桩系统源码

简述 SpringBoot 框架&#xff0c;充电桩平台充电桩系统充电平台充电桩互联互通协议云快充协议1.5新能源汽车电动自行车公交车-四轮车充电充电源代码充电平台源码Java源码无加密项目 介绍 云快充协议云快充1.5协议云快充协议开源代码云快充底层协议云快充桩直连桩直连协议充…

半导体器件制造5G智能工厂数字孪生物联平台,推进制造业数字化转型

半导体器件制造行业作为高科技领域的核心驱动力&#xff0c;正积极探索和实践以5G智能工厂数字孪生平台为核心的新型制造模式。这一创新不仅极大地提升了生产效率与质量&#xff0c;更为制造业的未来发展绘制了一幅智能化、网络化的宏伟蓝图。 在半导体器件制造5G智能工厂中&a…

每天五分钟计算机视觉:将人脸识别问题转换为二分类问题

本文重点 在前面的课程中,我们学习了两种人脸识别的网络模型,这两种人脸识别网络不能算是基于距离或者Triplet loss等等完成的神经网络参数的学习。我们比较熟悉的是分类任务,那么人脸识别是否可以转变为分类任务呢? 本节课程我们将介绍一种全新的方法来学习神经网络的参…

微服务架构陷阱与挑战

微服务架构6大陷阱 现在微服务的基础设施还是越来越完善了&#xff0c;现在基础设施缺乏的问题逐渐被解决了。 拆分粒度太细&#xff0c;服务关系复杂 拆分降低了服务的内部复杂度&#xff0c;但是提升了系统的外部复杂度&#xff0c;服务越多&#xff0c;服务和服务之间的连接…

java的Excel导出,数组与业务字典匹配并去掉最后一个逗号

♥️作者&#xff1a;小宋1021 &#x1f935;‍♂️个人主页&#xff1a;小宋1021主页 ♥️坚持分析平时学习到的项目以及学习到的软件开发知识&#xff0c;和大家一起努力呀&#xff01;&#xff01;&#xff01; &#x1f388;&#x1f388;加油&#xff01; 加油&#xff01…

Node-RED和物联网分析:实时数据处理和可视化平台

这篇论文的标题是《Node-RED and IoT Analytics: A Real-Time Data Processing and Visualization Platform》&#xff0c;发表在《Tech-Sphere Journal of Pure and Applied Sciences (TSJPAS)》2024年第一期上。论文主要探讨了Node-RED和物联网分析在物联网(IoT)实时数据处理…

Vue学习记录之六(组件实战及BEM框架了解)

一、BEM BEM是一种前端开发中常用的命名约定&#xff0c;主要用于CSS和HTML的结构化和模块化。BEM是Block、Element、Modifier的缩写。 Block&#xff08;块&#xff09;&#xff1a;独立的功能性页面组件&#xff0c;可以是一个简单的按钮&#xff0c;一个复杂的导航条&…

无法创建新的堆栈防护界面

&#x1f3c6;本文收录于《全栈Bug调优(实战版)》专栏&#xff0c;主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&am…

商业实战!深入剖析StableDiffusion 电商服装模特精准替换真人,虚拟模特变现教程

大家好&#xff0c;我是灵魂画师向阳 在之前的文章中&#xff0c;我们已经深入讲解了SD与ControlNet基础知识和原理。接下来我们将结合这一堆基础工具法宝组合使用&#xff0c;完成一些有意义的AIGC商业实战案例分享。也欢迎大家在文末留言建议希望了解的实战案例。 本文是来…

在基准测试和规划测试中选Flat还是Ramp-up?

Flat测试和Ramp-up测试是各有优势的&#xff0c;下面我们就通过介绍几种实用的性能测试策略来分析这两种加压策略的着重方向。 基准测试 基准测试是一种测量和评估软件性能指标的活动&#xff0c;通过基准测试建立一个已知的性能水平&#xff08;称为基准线&#xff09;&…

计算机毕业设计 美妆神域网站的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…