深入理解指针(1)

星夢妙者
发布: 2025-04-25 12:46:01
原创
279人浏览过

1. 内存和地址

1.1 内存

在讨论内存和地址之前,让我们从生活中的一个例子开始:假设你住在一个有100个房间的宿舍楼里,但这些房间没有编号。如果你的朋友来找你玩,他必须逐个房间寻找,这样效率很低。然而,如果我们根据楼层和房间的位置为每个房间编号,那么朋友只要知道房间号就能快速找到你。

将这个例子应用到计算机中,情况又如何呢?我们知道,计算机的CPU(中央处理器)在处理数据时,需要从内存中读取数据,并将处理后的数据存回内存。我们购买电脑时,通常看到的内存容量是8GB、16GB或32GB等。那么,如何高效管理这些内存空间呢?

实际上,内存被划分为一个个内存单元,每个内存单元的大小为一个字节。

计算机中常见的单位(补充):

一个比特位可以存储一个二进制的位1或者0。

每个内存单元相当于一个学生宿舍,一个字节的空间内可以容纳8个比特位,就像宿舍里的八人间,每个人代表一个比特位。每个内存单元都有一个编号(相当于宿舍的门牌号),有了这个编号,CPU就可以快速找到特定的内存空间。

在生活中,我们把门牌号称为地址;在计算机中,内存单元的编号也被称为地址。在C语言中,地址被称为指针。

因此,我们可以理解为:内存单元的编号 == 地址 == 指针。

深入理解指针(1)

1.2 如何理解编址

CPU访问内存中的某个字节空间,必须知道这个字节空间在内存中的位置。由于内存中的字节数量庞大,因此需要对内存进行编址(就像给众多宿舍编号一样)。计算机中的编址并不是记录每个字节的地址,而是通过硬件设计完成的。

钢琴和吉他上并没有标注“剁、来、咪、发、唆、拉、西”这样的信息,但演奏者依然能准确找到每个琴弦的位置。这是为什么呢?因为制造商已经在乐器的硬件层面上设计好了,并且所有演奏者都知道这种约定。

首先,必须理解计算机内部有很多硬件单元,这些硬件单元需要协同工作。所谓的协同,至少需要能够进行数据传递。然而,硬件与硬件之间是独立的,那么如何通信呢?答案很简单,用“线”连接起来。

CPU和内存之间也有大量的数据交互,因此,两者也必须用线连接。不过,我们今天关注一组线,称为地址总线。硬件编址也是如此。我们可以简单理解,32位机器有32根地址总线,每根线只有两种状态,表示0或1(电脉冲的有无),一根线就能表示两种含义,两根线就能表示四种含义,依此类推。32根地址线就能表示2^32种含义,每种含义都代表一个地址。地址信息被下达给内存,内存就可以找到该地址对应的数据,并通过数据总线将数据传回CPU的寄存器。

  1. 指针变量和地址

2.1 取地址操作符(&)

理解了内存和地址的关系后,我们再回到C语言。在C语言中,创建变量实际上是在内存中申请空间。例如:

深入理解指针(1)深入理解指针(1)

如上所示的代码创建了一个整型变量a,内存中申请了4个字节用于存放整数10,每个字节都有地址,如图所示,4个字节的地址分别是:

深入理解指针(1)

那么我们如何得到a的地址呢?这里需要学习一个操作符(&)——取地址操作符。

深入理解指针(1)深入理解指针(1)

虽然整型变量占用4个字节,但我们只要知道第一个字节的地址,就可以顺藤摸瓜访问到4个字节的数据。

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

2.2.1 指针变量

通过取地址操作符(&)得到的地址是一个数值,例如:0x006FFD70。这个数值有时也需要存储起来,以便后期使用。我们将这样的地址值存放在指针变量中。例如:

深入理解指针(1)

指针变量也是一种变量,这种变量用于存储地址,存放在指针变量中的值都被理解为地址。

2.2.2 如何拆解指针类型

这里pa左边写的是int是在说明pa是指针变量,而前面的int是在说明pa指向的是整型(int)类型的对象。

深入理解指针(1)

如果有一个char类型的变量ch,ch的地址应该放在什么类型的指针变量中呢?

2.2.3 解引用操作符

我们将地址保存起来,未来是要使用的,那么如何使用呢?

在现实生活中,我们使用地址找到一个房间,在房间里可以取走或存放物品。

C语言中也是如此,只要拿到了地址(指针),就可以通过地址(指针)找到指向的对象。这里必须学习一个操作符——解引用操作符(*)。

深入理解指针(1)

上面的代码中第7行就使用了解引用操作符,pa的意思是通过pa中存放的地址找到指向的空间,pa实际上就是a变量;所以*pa = 0,这个操作是将a改成了0。

如果目的是把a改成0,直接写成a = 0;不就行了,为什么还要使用指针呢?实际上,这里是将a的修改交给了pa来操作,这样对a的修改就多了一种途径,写代码会更加灵活,后期慢慢就能理解了。

2.3 指针变量的大小

前面的内容我们了解到,32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后是1或者0,我们把32根地址线产生的二进制序列视为一个地址,那么一个地址就是32个bit位,需要4个字节才能存储。

如果指针变量是用来存放地址的,那么指针变量的大小就必须是4个字节的空间。

同理,64位机器假设有64根地址线,一个地址就是64个二进制位组成的二进制序列,存储起来需要8个字节的空间,指针变量的大小就是8个字节。

深入理解指针(1)深入理解指针(1)

结论:

  • 在32位平台下,地址是32个bit位,指针变量大小是4个字节。
  • 在64位平台下,地址是64个bit位,指针变量大小是8个字节。
  • 注意,指针变量的大小与类型无关,只要是指针变量,在相同的平台下,大小都是相同的。
  1. 指针变量类型的意义

指针变量的大小与类型无关,只要是指针变量,在同一个平台下,大小都是一样的,为什么还要有各种各样的指针类型呢?

实际上,指针类型是有特殊意义的,我们接下来继续学习。

3.1 指针的解引用

对比以下两段代码,主要在调试时观察内存的变化。

深入理解指针(1)深入理解指针(1)

调试时我们可以看到,代码1会将n的4个字节全部改为0,但代码2只是将n的第一个字节改为0。

结论:指针的类型决定了,对指针解引用时有多大的权限(一次能操作几个字节)。

例如:char的指针解引用只能访问一个字节,而int的指针解引用可以访问四个字节。

3.2 指针+-整数

深入理解指针(1)

我们可以看出,char类型的指针变量+1跳过1个字节,而int类型的指针变量+1跳过了4个字节。这就是指针变量的类型差异带来的变化。指针+1实际上是跳过一个指针指向的元素。指针可以+1,也可以-1。

结论:指针的类型决定了指针向前或向后移动一步的距离。

3.3 void* 指针

在指针类型中,有一种特殊的类型是void类型,可以理解为无具体类型的指针(或者称为泛型指针),这种类型的指针可以用来接受任意类型的地址。但是也有局限性,void类型的指针不能直接进行指针的+-整数和解引用的运算。

深入理解指针(1)

在上面的代码中,将一个int类型的变量的地址赋值给一个char类型的指针变量。编译器给出了一个警告(如下图),因为类型不兼容。而使用void类型就不会有这样的问题。

深入理解指针(1)

使用void*类型的指针接收地址:

深入理解指针(1)

VS编译代码的结果:

深入理解指针(1)

这里我们可以看到,void*类型的指针可以接收不同类型的地址,但无法直接进行指针运算。

那么void*类型的指针有什么用呢?

一般void*类型的指针用于函数参数部分,用来接收不同类型数据的地址,这样的设计可以实现泛型编程的效果。使得一个函数可以处理多种类型的数据,在《深入理解指针(4)》中我们会详细讲解。

以上就是深入理解指针(1)的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号