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

深入理解指针part1

内存和地址

内存

内存是计算机中的系统资源,它有一个个的单元空间用于储存数剧,每个单元都有一个个属于自己的编号,

举个例子,有一栋学生宿舍有50个房间,你的朋友要去找你,可是他不知道要去哪找你,他就得挨家挨户的找,这样效率很低,可当你给了你的地址,他就能很快的找到你

在计算机中同理,CPU的数据需要去内存中读取,处理完数据后也要回传给内存,当我们买电脑时内存条又8/16/32G的储存空间,可要如何管理内存空间呢?

内存中有一个个的单元空间,每个单元空间都能容纳一个空间的大小

补充:内存中常见的存储单位

内存中的单元空间就像一户,里面可以容纳8个家庭成员,每一个家庭成员都是一bit

每个内存单元都会有编号(和家门前的门派哈一样),有了这个地址,CPU可以快速找到该单元空间

我们通常把门牌号叫做地址,地址在C语言中有个名字:指针

可以理解位内存的单元编号=地址=指针

如何理解编址

在计算机中有很多的硬件单元(显卡,CPU,内存条,主板等),他们之间需要相互协作(至少要能传输数据),可是各个硬件单元都是相互独立的,要怎么让他们互相协作呢?我们要使用“线”把他们链接起来,例如CPU和内存,两者要互相交换数据,所以要用“线”把他们连起来

CPU读取数据时需要访问空间单元时,需要知道空间单元在哪里,可是内存单元很多,所以需要编址(就像一栋公寓里有很多户,所以需要门牌号)

内存编址是在应建设计阶段就设计好的,就像一把吉他,上面并没有标记音阶,可使用者为什么可以在知道那个音在哪条音阶上,因为这把乐器在当初设计是就已经定好哪个地方,使用者再透过学习来熟悉乐器,本质上是约定出来的共识

在计算机中也是同样的道理,在32位元的机器中有32条地址总线,每条线中都有两种状态,表示0或1,一条线有两个状态两条线有四种状态.32条线就有2^32个状态,每个状态代表一个地址

当地址信息被传送到内存,在内存里就能快速的找到该地址里的数据再透过地址总线回传到CPU的寄存器

指针变量和地址

理解了内存和地址的关系后,我们得知道地址是如何

取地址操作符(&)

我们得知道在C语言中,创建一个变量时就是在向内存申请一个空间

我创建了a并在里面存放了20,当中的每个字节都有自己的地址

0x000000C0DDB6FB34  
0x000000C0DDB6FB35 
0x000000C0DDB6FB36 
0x000000C0DDB6FB37 

可要怎么知道a的地址呢?这个时候就要说取地址操作符

取地址操作符(&)是专门读取数据所在的单元空间的地址,例如上面20的地址,我们先将他打印出来

注:打印地址的时候要使用%p

int main()
{int a = 20;printf("%p", &a);return 0;
}

从&a取出的地址是四个字节中最小的地址,虽说只打印出最小的地址,但要顺藤摸瓜找其他地址也是可行的

指针变量引用操作符(*)

我们透过取地址操作符取得数据所在地址的数值,例如:0x000000C0DDB6FB34,那我们要怎么讲数据储存起来然后再使用,那我们要把地址放在哪?存放在指针变量中

int main()
{int a = 20;int* pa = &a;return 0;
}

指针变量也是变量,pa就是专门来储存地址的,只要存放在指针变量中的都是地址

当我们了解了存取之后,我们所看到的int*又是啥?

这里我们可以拆分位int 和 * ,我们可以这么理解,int是数据类型,*则是表示 pa 为指针变量

int a = 20;      //用于储存变量
int* pa = &a;    //用于储存指针变量

解引用操作符(*)

当地址储存在指针变量中后,我们会需要将他们拿出来使用,就像东西放在仓库中,有一天要拿出来使用,数据也一样,这时候就要说解引用操作符(*)

解引用操作符是用于读取地址中数据,在C语言中,只要有地址就能透过地址找到相对的数据,我们来写一段代码

int main()
{int a = 100;int* pa = &a;*pa = 0;return 0;
}

上述代码使用了解引用操作符,*pa是透过pa存放的地址找到存放数据的空间,所以*pa就是a的变量,pa不过是将a变成0而已

指针变量的大小

我们前面了解到32位元的机器有32根地址总线,同理,64位元就有64根地址总线,我们将每一根地址总线当作2进制的序列当做一个地址,一个字节为8个bit,那32根线就有32个bit就有4个字节,64根地址线有64个bit就有8个字节,一个我们先写一段代码:

int main()
{int a = 40;int* pa = &a;char c = 's';char* pc = &c;printf("%zd\n", sizeof(pa));printf("%zd\n", sizeof(pc));return 0;
}

这段代码分别为使用x64(64位元)和x86(32位元)来运行

x64:

x86:

我们发现不论int 或是char 在x64都为8位元在x86都为4位元,为啥?这是因为指针变量和类型大小无关,只要是指针变量,在同个平台下数字都是相同的

指针变量类型的意义

指针的解引用

我先编一个地址,地址为0x15474577,我们来写几段代码

int main()
{int a = 0x15474577;int* pa = &a;*pa = 0;return 0;
}

 

int main()
{int a = 0x15474577;short* pa = &a;*pa = 0;return 0;
}

我们发现第一段代码四个位元都变成了0而第二段代码只变了两个数字,这是为啥?我使用int类型的数据储存地址,int类型的指针变量有四个空间,所以全部的数据都能被改动,可是short类型的指针变量只能访问两个字节空间所以只有两个数据被改动,所以我们得知每个指针数据类型的权限是不同的

指针+-变数

我们来写一段代码

int main()
{int a = 10;short* pb = (short*)&a;int* pa = &a;printf("%p\n", &a);printf("%p\n", pa);printf("%p\n", pa + 1);printf("%p\n", pb);printf("%p\n", pb + 1);return 0;
}

我们发现pa+1地址跳了四个,pb+1的地址只跳了两个,这是为什么?

因为这里的的+1是指加一个指针变量类型的空间(当然也能-1),int占了四个字节而short只占两个字节,所以int才从8跳到C,而short只从8跳到a

结论就是,每向前和后退一次的距离是有指针的数据类型决定的

http://www.xdnf.cn/news/587791.html

相关文章:

  • 【Django ORM】三万字了解Django ORM的基本概念和基本使用
  • 并发编程之并发协同工具类
  • ollama+open-webui搭建可视化大模型聊天
  • 【计算机网络】TCP如何保障传输可靠性_笔记
  • Python结合ollama和stramlit开发聊天机器人
  • 栈和队列总结
  • ISO 26262-5 生产维护和报废
  • 前端性能优化的秘密武器:Preload 与 Prefetch 的深度解析
  • fatal error: uuid/uuid.h: No such file or directory 编译问题修复。
  • VS Code中Maven未能正确读取`settings.xml`中配置的新路径
  • 将MCP(ModelContextProtocol)与Semantic Kernel集成(调用github)
  • [密码学实战]使用C语言实现TCP服务端(二十九)
  • SAR ADC 的常见架构
  • 广州能源所重大突破:闪蒸焦耳加热助力粉煤灰 / 赤泥中关键金属低碳回收
  • Netty学习专栏(一):Java NIO编程与核心组件详解
  • Android View的事件分发机制
  • docker容器暴露端口的作用
  • kafka在线增加分区副本数
  • RK3588 RGA 测试
  • 工商业预付费系统组成架构及系统特点介绍
  • 【MySQL成神之路】MySQL插入、删除、更新操作汇总
  • Unity Shader入门(更新中)
  • python安装与使用
  • Java的列表、集合、数组的添加一个元素各自用的什么方法?
  • 【论文阅读】——AN EXPRESSIVE REPRESENTATION OF GENERAL 3D SHAPES
  • Linux环境基础开发工具->vim
  • 实现FAT12文件管理
  • 线性回归模型的参数估计
  • AutoMapper .net Framework 的 Model转换扩展方法
  • python学习 day5