当前位置: 首页 > news >正文

那些年与指针的爱恨情仇(一)---- 指针本质及其相关性质用法


关注小庄 顿顿解馋 (≧∇≦)

引言: 小伙伴们在学习c语言过程中是否因为指针而困扰,指针简直就像是小说女主,它逃咱追,我们插翅难飞…本篇文章让博主为你打理打理指针这个傲娇鬼吧~
本节我们将认识到指针本质,何为指针和她的性质用法,请放心食用!

指针本质及其性质用法

  • 一.指针的本质
    • 1.🏠 内存与地址
    • 2.🏠 指针变量与地址
      • 2.1 取址符(&)
      • 2.2指针变量
      • 2.3解引用操作符(*)
      • 2.4指针变量的大小
  • 二.指针的性质
    • 1.🏠指针类型的意义
      • 1.1 不同指针类型的解引用
      • 1.2指针+-整数
    • 2.🏠 const修饰指针
      • 2.1 const修饰变量
      • 2.2 const修饰指针变量
    • 3.🏠 指针运算
      • 3.1 指针+-整数
      • 3.2 指针-指针
      • 3.3指针的关系运算
  • 三.指针的使用
    • 1.🏠 野指针
    • 3.🏠assert断言
    • 4.🏠传值调用和传址调用
    • 在这里插入图片描述

一.指针的本质

1.🏠 内存与地址

内存单元:我们在学校寄宿时是几个人为一个集体为宿舍的,为了方便入住以及内务检查学校给每个宿舍设置了门牌号方便辨认。类似地,存储数据的内存划分一个一个内存单元同时给它们编上“号”方便取出数据。

内存单元:内存最小单元,单位是字节也就是8个bit。

地址:正如上言所说,我们给内存单元的编号就是地址,在c语言中我们叫它指针

总结: 指针 == 地址 == 内存单元编号 他是我们用来取数据的东西

2.🏠 指针变量与地址

数据在内存中的地址就是指针,类比之前我们学习过的数据类型,整形变量是存储整形的变量,那么指针变量就是存指针的变量

我们知道指针变量了先不着急,我们知道指针变量存的是地址,赋值两边要相同类型你知道怎么获得地址吗?

2.1 取址符(&)

取址符顾名思义就是取出地址,打印地址时我们用的是%p,在我们编译器如何查看地址呢?接下来上图

在这里插入图片描述
在vs上进入调试模式->调试->窗口->内存/监视窗口都可查看内存,别忘记加上我们的取址符哦

2.2指针变量

指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址,他的定义如下:

Datatype * name;
int a = 10;
int * pf = &a;
char c = 'c';
char * pc = &c;

理解:这里的*用来表示这是一个指针变量,前面的Datatype表示的是指针指向的数据类型,而去掉变量名就是这个指针变量的类型,比如int* pf表示pf是个指针变量它指向的数据类型是int,存储的是int类型数据的地址,指针类型是int *

类比地,聪明的你应该也会类比其他数据类型了吧~

void*类型
在指针类型中有这么一个存在,它可以可以理解为⽆具体类型的指针(或者叫泛型指针),这种类型的指针可以⽤来接受任意类型地址。就像一个公共场所。

2.3解引用操作符(*)

有的小伙伴到这里可能要问了既然知道什么是指针变量,那他怎么用啊?快端上来罢!这里我们就要了解我们的解引用操作符了

客人房间(变量)房间编号(地址)
变量a0x11223344
变量b0x12332166
变量c0x12ffaa11

如上表所示,每个变量(房间)都对应一个编号(地址),而要进入房间使用它就得用解引用操作符

int a = 1;
printf("%d ",a);
int *p = &a;
*p = 10;
printf("%d",a);

输出结果是1 10

总结: 解引用操作符(*)作用是通过地址,以地址为媒介,找到地址指向的空间对该空间进行操作。

2.4指针变量的大小

我们知道不同的数据类型所占字节大小不同,那不同的指针类型是不是也是大小不同呢?接下来我们先普及一下编址的相关知识

在这里插入图片描述
CPU与内存之间是有大量数据交互的。大致过程如下:CPU先通过控制总线下命令->地址总线收到命令找对应地址->通过数据总线传输数据。在这个过程中地址总线不只图中一条哦,作者只是大概画出来。不同机器地址总线总数不同,比如32位机器有32条,64位有64条,每根线只有两态,表⽰0,1【电脉冲有⽆】,那么⼀根线,就能表⽰2种含义,2根线就能表⽰4种含义,依次类推。32根地址线,就能表⽰2^32种含义,每⼀种含义都代表⼀个地址

通过上面的知识普及我们了解到地址总线发出的电信号转换成二进制序列就是我们的地址,所以不同的机器就会产生不同位的二进制序列当做地址,32位地址->32个bit->4个字节,64位地址->64个bit->8个字节。

printf("%zd\n", sizeof(char *));
printf("%zd\n", sizeof(short *));
printf("%zd\n", sizeof(int *));
printf("%zd\n", sizeof(double *));

x86(32位环境) 输出结果为4 4 4 4
x64(64位环境) 输出结果为8 8 8 8

总结: 指针变量大小与它指向的数据类型无关,而与使用的机器有关

二.指针的性质

1.🏠指针类型的意义

既然指针变量大小与指针类型无关,那为什么还要设置这么多类型的指针变量?有句话叫做存在即合理,设置了它肯定是有它的意义的。

1.1 不同指针类型的解引用

//代码1
int n = 0x11223344;
int *pi = &n;
*pi = 0;
//代码2
int n = 0x11223344;
char* pc = &n;
*pc = 0;

两段代码都是存了n的地址,只不过是不同指针类型,那是否值都变为0了呢?我们用之前的查看内存知识观察下。
在这里插入图片描述
通过上面观察我们发现不同指针类型得出的结果也不同,由此我们可以得出结论:不同指针类型解引用权限不同,也就是能操作字节个数不同,正如上面的例子int解引用能将4个字节都置为0,而char解引用只能将1个字节置为0.

1.2指针±整数

int n = 10;
char *pc = (char*)&n;
int *pi = &n;
printf("%p\n", &n);
printf("%p\n", pc);
printf("%p\n", pc+1);
printf("%p\n", pi);
printf("%p\n", pi+1)

输出结果:
004FF700
004FF700
004FF701
004FF700
004FF704

我们可以看出, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。
这就是指针变量的类型差异带来的变化。

结论:指针类型还决定了指针向前或向后走一步有多大。
注:之前提到的void*虽然能接收各种类型指针,但却不能使用指针加减也不能解引用,可谓是上天为你关了一扇门也为你打开一扇窗啊。

2.🏠 const修饰指针

2.1 const修饰变量

变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。
但是如果我们希望⼀个变量加上⼀些限制,不能被修改,怎么做呢?这就是const的作⽤。

int m = 0;
m = 20;//m是可以修改的
const int n = 0;
n = 20;//n是不能被修改的

加上const后n不能动了,那我们运用之前的解引用呢?

const int n = 0;
printf("n = %d\n", n);
int*p = &n;
*p = 20;
printf("n = %d\n", n);

我们可以看到这⾥⼀个确实修改了,但是我们还是要思考⼀下,为什么n要被const修饰呢?就是为了
不能被修改,如果p拿到n的地址就能修改n,这样就打破了const的限制,这是不合理的,所以应该让
p拿到n的地址也不能修改n,那接下来怎么做呢?

2.2 const修饰指针变量

我们给上几段代码来测试下

//代码1 无const
int n = 10;
int m = 20;
int *p = &n;
*p = 20;//能否赋值
p = &m; //能否赋值
//代码2  const在*左边
int n = 10;
int m = 20;
const int *p = &n;
*p = 20;//能否赋值
p = &m; //能否赋值
//代码3 const在*右边
int n = 10;
int m = 20;
int *const p = &n;
*p = 20;//能否赋值
p = &m; //能否赋值
//代码4 const在*左边和右边
int n = 10;
int m = 20;
int const *const p = &n;
*p = 20;//能否赋值
p = &m; //能否赋值

代码1 :都能赋值
代码2 : 语句1不能赋值
代码3 :语句2不能赋值
代码4: 两语句都不能赋值

总结:1.const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变,但指针变量本身的内容可变。2.const如果放在*的右边,修饰的是指针变量本身的内容,保证存储的内容不能修改,但指针指向内容可通过指针修改3.const在*的左边和右边则指针变量本身内容和指针指向内容都不能被修改。

总的来说,const修饰变量具有常量属性,但解引用相当于绕道找后门进入这个变量修改变量的值。此时用const修饰指针相当于把这个后门也关了

3.🏠 指针运算

  1. 指针±整数
  2. 指针-指针
  3. 指针关系运算

3.1 指针±整数

根据我们前面分析,指针±整数表示不同指针向前一步或向后一步的大小。这在数组可以很好运用,我们知道数组在内存中是连续存放的,这样知道其中一个元素地址再运用指针±整数就能得到其他元素地址,我们可以用它来打印数组

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
int i = 0;
int sz = sizeof(arr)/sizeof(arr[0]);
for(i=0; i<sz; i++)
{
printf("%d ", *(p+i));//p+i 这⾥就是指针+整数
}

3.2 指针-指针

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
printf("%d",&arr[2]-&arr[0]);
printf("%d",&arr[0]-&arr[2]);

输出结果为2 -2
我们可以得出指针-指针的绝对值得到的是指针与指针之间的元素个数,可以类比日期-日期是天数,而指针-指针、就是元素个数。前面的指针±也可以这样类比,日期加天数还是日期

注意:该运算的前提条件是相同类型的指针变量

3.3指针的关系运算

指针也是可以比较大小的,比较的是在内存位置的高低。

三.指针的使用

  接下来我们来了解一些关于指针的使用和注意事项吧

1.🏠 野指针

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的),通常以下三种情况会造成野指针。

  1. 指针未初始化
  2. 指针越界访问
  3. 指针指向空间释放(一般指针调用函数后内存空间被回收)
//原因3
int* test()
{
int n = 100;
return &n;
}
int main()
{
int*p = test();
printf("%d\n", *p);
return 0;
}

这个地方*p接收了返回的&n地址,但是调用函数后n就被释放了,也就是我们常说的空有其表

规避方法:

  1. 指针初始化为NULL
  2. 小心越界访问
  3. 不要返回局部变量地址
  4. 指针不用了及时置为NULL

3.🏠assert断言

assert.h 头⽂件定义了宏 assert() ,用于在运⾏时确保程序符合指定条件,如果不符合,就报错终⽌运行这个宏常常被称为“断⾔”。assert() 宏接受⼀个表达式作为参数。如果该表达式为真(返回值⾮零), assert() 不会产⽣任何作⽤,程序继续运⾏。如果该表达式为假(返回值为零), assert() 就会报错,在标准错误stderr 中写⼊⼀条错误信息,显⽰没有通过的表达式,以及包含这个表达式的⽂件名和⾏号。

好处:
1.能⾃动标识⽂件和出问题的⾏号
2.有⽆需更改代码就能开启或关闭 assert() 的机制。(如果已经确认程序没有问题,不需要再做断⾔,就在 #include <assert.h> 语句的前⾯,定义⼀个宏NDEBUG)

4.🏠传值调用和传址调用

我们知道函数传值调用时形参是实参的一份临时拷贝,改变形参不影响实参,那有什么方法能改变我们的实参呢?答案是我们的传址调用。

oid Swap2(int*px, int*py)
{
int tmp = 0;
tmp = *px;
*px = *py;
*py = tmp;
}
int main()
{
int a = 10;
int b = 20;
printf("交换前:a=%d b=%d\n", a, b);
Swap1(&a, &b);
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}

输出结果
交换前 a=10 b=20
交换后 a=20 b=10

由此可以得出,传址调用本质上也是传值,也是拷贝地址的值传到函数内部,只不过地址指向的空间是唯一的,我们便能通过地址来改变它的空间。值得注意的是,在函数内部创建的px,py跟a,b不是同个变量只不过他们存储的值(地址)一样,存储的值能干的事一样!

在这里插入图片描述

本次分享到这里就结束了,希望小伙伴能够学到知识,喜欢的话给小庄来个三连吧!

相关文章:

那些年与指针的爱恨情仇(一)---- 指针本质及其相关性质用法

关注小庄 顿顿解馋 (≧∇≦) 引言&#xff1a; 小伙伴们在学习c语言过程中是否因为指针而困扰&#xff0c;指针简直就像是小说女主&#xff0c;它逃咱追&#xff0c;我们插翅难飞…本篇文章让博主为你打理打理指针这个傲娇鬼吧~ 本节我们将认识到指针本质&#xff0c;何为指针和…...

计算机网络——TCP协议

&#x1f4a1;TCP的可靠不在于它是否可以把数据100%传输过去&#xff0c;而是 1.发送方发去数据后&#xff0c;可以知道接收方是否收到数据&#xff1b;2.如果接收方没收到&#xff0c;可以有补救手段&#xff1b; 图1.TCP组成图 TCP的可靠性是付出代价的&#xff0c;即传输效率…...

软考高级有意义吗?

有同学在平台向我提问&#xff0c;软考高级好像不好通过&#xff0c;花那么多时间去准备&#xff08;非科班&#xff09;&#xff0c;有意义么&#xff1f; 我知道有些同学还在犹豫&#xff0c;不确定是否要报名软考系统架构设计师或者系统分析师。我认为&#xff0c;这种犹豫…...

二分算法模版

二分算法模版 实数二分算法模版实数二分模版题 整数二分算法模版向上取整二分模版向下取整二分模版二分模版的注意点二分模版中check函数的实现能够使用二分的条件 二分主要分两类&#xff0c; 一类是对实数进行二分&#xff0c;一类是对整数进行二分 对整数二分又分成2种&…...

【CSS】字体效果展示

测试时使用了Google浏览器。 1.Courier New 2.monospace 3.Franklin Gothic Medium 4.Arial Narrow 5.Arial 6.sans-serif 7.Gill Sans MT 8.Calibri 9.Trebuchet MS 10.Lucida Sans 11.Lucida Grande 12.Lucida Sans Unicode 13.Geneva 14.Verdana 15.Segoe UI 16.Tahoma 17.…...

asp.net宠物流浪救助系统

asp.net宠物流浪救助系统 当领养人是无或者未领养的时候&#xff0c;就会显示领养申请按钮&#xff0c;登陆的用户可以申请领域该宠物&#xff0c;未登录会提示登陆然后转到登陆页面 宠物领养页面支持关键字查询符合条件的宠物 当有领养人时就隐藏领养申请按钮 社区交流意见…...

git常见命令

1、常用命令记录 1&#xff09;切换分支 git checkout 分支名2&#xff09;查看分支 查看远程分支 git branch -r 查看所有分支包括本地分支和远程分支 git branch -a3&#xff09;合并分支 git merge 来源分支4&#xff09;删除分支 删除本地分支&#xff1a;git branch …...

主成分分析(PCA)Python

实际问题研究中&#xff0c;常常遇到多变量问题&#xff0c;变量越多&#xff0c;问题往往越复杂&#xff0c;且各个变量之间往往有联系。于是&#xff0c;我们想到能不能用较少的新变量代替原本较多的旧变量&#xff0c;且使这些较少的新变量尽可能多地保留原来变量所反映的信…...

Leetcode—144. 二叉树的前序遍历【简单】

2023每日刷题&#xff08;九十六&#xff09; Leetcode—144. 二叉树的前序遍历 实现代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr…...

混淆矩阵、准确率、查准率、查全率、DSC、IoU、敏感度的计算

1.背景介绍 在训练的模型的时候&#xff0c;需要评价模型的好坏&#xff0c;就涉及到混淆矩阵、准确率、查准率、查全率、DSC、IoU、敏感度的计算。 2、混淆矩阵的概念 所谓的混淆矩阵如下表所示&#xff1a; TP:真正类&#xff0c;真的正例被预测为正例 FN:假负类&#xf…...

ChatGPT目前的AI一哥

ChatGPT和文心一言是两个不同的AI助手&#xff0c;各自有其独特的特点和应用场景。以下是对它们在智能回复、语言准确性和知识库丰富度等方面的简要比较&#xff1a; 智能回复&#xff1a;ChatGPT是由OpenAI开发的语言模型&#xff0c;具有强大的自然语言处理和生成能力&#x…...

认识思维之熵

经常有读者问我&#xff0c;说&#xff1a; 为什么向您请教一个问题&#xff0c;您总能很快指出在哪篇文章里面提到过&#xff0c;是因为您的记忆力特别好吗&#xff1f; 其实不是的。更重要的原因是&#xff1a;如果你经过系统训练&#xff0c;有意识地去获取知识的话&#x…...

蓝桥杯备战——1.点亮LED灯

1.解析原理图 由上图可以看到8个共阳LED灯接到了573输出口&#xff0c;而573输入接到单片机P0口上。当573 LE脚输入高电平时&#xff0c;输出随输入变化&#xff0c;当LE为低电平时&#xff0c;输出锁存。 由上图可以看到Y4C接到了或非门74HC02的输出端&#xff0c;而输入端为…...

【网络协议测试】畸形数据包——圣诞树攻击(DOS攻击)

简介 TCP所有标志位被设置为1的数据包被称为圣诞树数据包&#xff08;XMas Tree packet&#xff09;&#xff0c;之所以叫这个名是因为这些标志位就像圣诞树上灯一样全部被点亮。 标志位介绍 TCP报文格式&#xff1a; 控制标志&#xff08;Control Bits&#xff09;共6个bi…...

Java基础面试题-5day

泛型 什么是泛型&#xff1f;有什么用&#xff1f; 泛型是jdk5引入的新特性&#xff0c;通过泛型可以提高代码的可读性和稳定性&#xff1b;当我们使用泛型时&#xff0c;传入的对象类型必须是指定的泛型类型&#xff0c;否则就会报错 泛型的使用方式有哪些&#xff1f; 一…...

软通智慧启动鲲鹏原生应用开发合作

1月25日&#xff0c;软通智慧科技有限公司启动鲲鹏原生应用开发合作&#xff0c;将基于鲲鹏硬件底座、openEuler、开发套件Kunpeng DevKit和应用使能套件Kunpeng BoostKit开展面向智慧园区、政务、水利水务等行业场景的软硬件原生应用开发&#xff0c;并持续发布性能更优的鲲鹏…...

【STM32】STM32F4中USB的CDC虚拟串口(VCP)使用方法

文章目录 一、前言二、STM32CubeMX生成代码2.1 选择芯片2.2 配置相关模式2.3 设置时钟频率2.4 生成代码2.5 编译并下载代码2.6 结果2.7 问题 三、回环测试3.1 打开工程3.2 添加回环代码3.3 编译烧录并测试 四、出现问题和解决方法4.1 烧录总是要自己插拔USB4.2 自己生成的工程没…...

网络协议与攻击模拟_06攻击模拟SYN Flood

一、SYN Flood原理 在TCP三次握手过程中&#xff0c; 客户端发送一个SYN包给服务器服务端接收到SYN包后&#xff0c;会回复SYNACK包给客户端&#xff0c;然后等待客户端回复ACK包。但此时客户端并不会回复ACK包&#xff0c;所以服务端就只能一直等待直到超时。服务端超时后会…...

CPU,内存和硬盘之间的关系

计算机三大件&#xff1a;CPU&#xff0c;内存&#xff0c;硬盘。从运算速度来看&#xff0c;CPU>内存>固态硬盘>机械硬盘。 电脑卡顿怎么解决&#xff1f; 1、清理垃圾&#xff1b; 2、释放C盘空间&#xff0c;因为系统需要C盘空间当作虚拟内存&#xff1b; 3、增…...

Java面试题之基础篇

文章目录 一&#xff1a;谈谈你对面向对象的理解二&#xff1a;JDK、JRE、JVM三者区别和联系三&#xff1a;和equals比较四&#xff1a;hashCode与equals五&#xff1a;final六&#xff1a;String、StringBuffer、StringBuilder七&#xff1a;重载与重写的区别&#xff1f;八&a…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...