news 2026/8/11 2:25:38

指针核心知识(上)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
指针核心知识(上)


第 11 章:指针核心知识(上)

  • 第 11 章:指针核心知识(上)
  • 1. 阅读前问题卡:C 语言指针核心知识(上)
    • 1.1 阅读前先看这几个问题
    • 1.2 读完后完成这 3 个任务
      • 1.2.1 任务 1:梳理内存、地址与指针
      • 1.2.2 任务 2:运行取址与解引用示例
      • 1.2.3 任务 3:拆解指针类型与大小
    • 1.3 自检清单
  • 2. 目标
  • 3. 内存和地址
  • 4. 指针变量和地址
    • 4.1 取地址操作符(&)
    • 4.2 指针变量
      • 4.2.1 指针变量和初始化
      • 4.2.2 如何拆解指针类型
      • 4.2.3 解引用操作符
    • 4.3 指针变量的大小


1. 阅读前问题卡:C 语言指针核心知识(上)

建议先读:先读第 3 节建立“内存单元—地址—指针”的关系,再按 4.1、4.2.1、4.2.3 节理解取址、保存地址和解引用。

暂时跳过:掌握取地址、指针初始化和解引用后,再细看 4.3 节的数据模型表。


1.1 阅读前先看这几个问题

  1. 文档怎样从“房间号”类比推导出内存单元、地址和指针的关系?
  2. CPU 读写数据时,虚拟地址、MMU 和物理内存之间是什么关系?哪些特殊领域可能直接使用物理地址?
  3. 对整型变量a使用&a会得到什么?为什么只记录其 4 个字节中地址较小的那个地址就够了?
  4. 指针变量保存的是什么?NULL指针与未初始化的局部指针分别表示什么,又有什么使用风险?
  5. 声明int *pa时,*int分别说明什么?它与表达式*pa中的解引用操作符有什么区别?
  6. *pa = 0为什么能够修改a?这条语句体现了地址、指针变量和被指向对象怎样的联系?
  7. 指针变量的大小由什么决定?为什么不同指针类型在同一平台下大小相同,而 X86 与 X64 的测试结果不同?


1.2 读完后完成这 3 个任务


1.2.1 任务 1:梳理内存、地址与指针
  • 做什么:画出“内存单元—地址—指针—CPU”的关系,并在图中补上虚拟地址经 MMU 映射到物理内存的方向。
  • 验证标准:能说明一个内存单元为 1 个字节、一个字节含 8 个比特位,并能区分程序调试时看到的虚拟地址与物理地址。


1.2.2 任务 2:运行取址与解引用示例
  • 做什么:运行文档中的取地址和解引用示例,观察&apaa的值,再执行*pa = 0
  • 验证标准:能确认pa保存&a的结果,%p输出变量地址,并观察到通过*pa赋值后a的值变为 0。


1.2.3 任务 3:拆解指针类型与大小
  • 做什么:为char ch = 'w';写出匹配的指针声明,再运行sizeof示例比较 X86 与 X64 环境中的各类指针大小。
  • 验证标准:能解释声明中*与前置类型各自的含义,并对照运行结果说明 X86 环境下各类指针为 4 个字节、X64 环境下为 8 个字节。


1.3 自检清单

  • 我能用一句话说明地址与指针的关系。
  • 我能解释内存为何按字节划分并为每个内存单元编号。
  • 我能区分虚拟地址、物理地址及 MMU 的映射作用。
  • 我能说明&、指针变量和解引用操作符*在示例中的职责。
  • 我能区分NULL指针与未初始化的局部野指针。
  • 我能拆解int *pa的类型含义,并说明指针大小与平台数据模型的关系。


2. 目标

  1. 理解指针的本质就是地址。
  2. 理解指针类型的意义,能够使用正确的指针类型。
  3. 理解指针运算的本质就是地址的偏移。
  4. 掌握传值调用和传址调用的区别。
  5. 掌握二级指针。
  6. 为后续指针进阶打好基础。


3. 内存和地址

学习指针必须先理解内存和地址。

在讲解之前,先看一个生活中的案例:

假设有一栋宿舍楼,把你放在楼里,楼上有 100 个房间,但是房间没有编号。你的一个朋友来找你玩,如果想找到你,就得挨个房子去找,这样效率很低。但是,如果我们根据楼层和楼层的房间情况,给每个房间编上号,如:

一楼:101,102,103... 二楼:201,202,203... ...

有了房间号,如果你的朋友得到房间号,就可以快速地找到房间,找到你。

生活中,每个房间有了房间号,就能提高效率,能快速地找到房间。

如果把上面的例子对照到计算机中,又是怎么样呢?

我们知道,计算机上 CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中。

常见的一台电脑上内存是 8 GB/16 GB/32 GB 等,那这些内存空间如何高效地管理呢?

解决办法和上面的例子相似:把内存划分为一个个的内存单元,每个内存单元的大小取 1 个字节,然后给每一个内存单元一个编号,就类似于房间编号。

计算机中常见的单位介绍:

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

bit - 比特位 Byte - 字节 KB MB GB TB PB
1Byte = 8bit 1KB = 1024Byte 1MB = 1024KB 1GB = 1024MB 1TB = 1024GB 1PB = 1024TB ...

🎈 注意,上面我们是为了方便讲解,对真实的内存访问进行了简化。实际上,我们编写的运行在 X86 架构的计算机上的 C 语言程序,是不会直接访问物理内存的。

程序运行时为变量分配的地址,在程序调试过程中,我们看到的地址都是虚拟地址;虚拟地址不是物理地址。虚拟地址是虚拟地址空间中的地址。

虚拟地址空间可以看作操作系统为每个程序提供的一个“假想的、独占的、连续的内存空间”。

程序中的虚拟地址,会由 CPU 的内存管理单元(MMU)映射到物理内存中,其实程序最终还是会访问物理内存。这个由虚拟地址换成物理地址的过程,暂时我们不需要关心,学习到操作系统的知识,慢慢再理解就可以了。

我们在学习编程语言的时候,都是在使用虚拟地址的。

🎈 是否存在 C 程序直接使用物理内存的情况?

有,但属于非常特殊的领域:

  • 嵌入式系统:没有 MMU(叫单片机或 MCU,如 ARM Cortex-M 系列、Arduino)。C 程序直接使用物理地址。此时,编译器和程序员要亲自管理整个内存布局。
  • 操作系统内核:内核拥有最高权限,它可以访问物理地址。


4. 指针变量和地址


4.1 取地址操作符(&)

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

X86 环境:

#include<stdio.h>intmain(){inta=10;return0;}

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

0x006FFD70 0x006FFD71 0x006FFD72 0x006FFD73

那我们如何能得到a的地址呢?

这里就得学习一个操作符&——取地址操作符。

X86 环境:

#include<stdio.h>intmain(){inta=10;&a;//取出a的地址printf("%p\n",&a);return0;}

按照我画图的例子,会打印出来:006FFD70

&a取出的是a所占 4 个字节中地址较小的字节的地址。

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


4.2 指针变量


4.2.1 指针变量和初始化

我们通过取地址操作符&拿到的地址是一个数值,比如0x006FFD70。这个数值有时候也需要存储起来,方便后期再使用。那我们把这样的地址值存放在哪里呢?答案是:指针变量中。

比如:

#include<stdio.h>intmain(){inta=10;int*pa=&a;//取出a的地址并存储到指针变量pa中return0;}

🎈 指针变量也是一种变量,这种变量就是用来存放地址的,存放在指针变量中的值都会理解为地址。

这里我们是拿&a(也就是a的地址)给pa进行了初始化。

有时候我们创建了一个指针变量,但是暂时不需要指向变量,可以使用NULL来初始化。NULL是 C 语言中定义的一个标识符常量,值是 0。0 也是地址,这个地址是无法使用的,读写该地址会报错。

#ifdef__cplusplus#defineNULL0#else#defineNULL((void*)0)#endif

一个指针变量被初始化为NULL,意思就是这个指针变量暂时不指向任何有效的内存位置。等后续使用的时候,可以给它赋值新的地址。

使用NULL初始化指针变量:

intmain(){int*pa=NULL;return0;}

❓ 那如果一个指针变量没有初始化,会怎样呢?

  • 指针变量如果是全局的,则默认会被初始化为 0,也就是NULL指针。
  • 指针变量如果是局部的,则指针变量的值是未知的,这样的指针就是野指针;强制使用野指针会导致非法访问内存,程序崩溃。


4.2.2 如何拆解指针类型

我们看到pa的类型是int*,该如何理解指针的类型呢?

inta=10;int*pa=&a;

这里pa左边写的是int*int*pa的类型;但是*int应该分开理解。

*是在说明pa是指针变量,而前面的int是在说明pa指向的是整型int类型的对象。

那如果有一个char类型的变量chch的地址要放在什么类型的指针变量中呢?

charch='w';pc=&ch;//pc的类型怎么写呢?


4.2.3 解引用操作符

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

程序中我们将地址保存起来,未来是要使用的,那怎么使用呢?C 语言中其实也是一样的。我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针)指向的对象。这里必须学习一个操作符,叫解引用操作符(*)。

#include<stdio.h>intmain(){inta=100;int*pa=&a;*pa=0;return0;}

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

有同学肯定在想,这里如果目的就是把a改成 0 的话,写成a = 0;不就完了,为啥非要使用指针呢?

其实这里是把a的修改交给了pa来操作。这样对a的修改就多了一种途径,写代码就会更加灵活,后期慢慢就能理解了。


4.3 指针变量的大小

C 语言标准并未规定指针变量的长度。我们现在分别在 VS 2026 的 X86 和 X64 环境下测试:

#include<stdio.h>intmain(){printf("%zd\n",sizeof(char*));printf("%zd\n",sizeof(short*));printf("%zd\n",sizeof(int*));printf("%zd\n",sizeof(double*));return0;}

X86 环境输出结果,X64 环境输出结果。

其实指针变量的大小是取决于数据模型的,下面列出了常见的 4 个数据模型:

数据模型指的是在特定操作系统、CPU 架构和 ABI(应用程序二进制接口)下,C/C++ 等语言中基本整数类型(shortintlonglong long)以及指针类型的具体位宽(字节数)的约定。

数据模型intlonglong longpointer(指针)操作系统
LP3216 位32 位64 位32 位Win16 API
ILP3232 位32 位64 位32 位Win32 API;Unix 及类 Unix 系统(Linux、Mac OS X)
LLP6432 位32 位64 位64 位Win64 API
LP6432 位64 位64 位64 位Unix 与类 Unix 系统(Linux、Mac OS X)

结论:

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

5.回答问题卡问题

问题1:文档怎样从“房间号”类比推导出内存单元、地址和指针的关系?

答:

一个内存单元类似于一个独立房间,内存地址就是房间门牌号,指针变量就是是专门保存地址值的变量代表存放房间号的记事本,指针本质上是地址。每个内存单元为 1 个字节,一个字节包含 8 个比特位。

问题2:CPU 读写数据时,虚拟地址、MMU 和物理内存之间是什么关系?哪些特殊领域可能直接使用物理地址?

程序中虚拟地址,会由CPU的内存管理单元(MMU)映射物理内存中,其实程序最终还是会访问物理内存。没有MMU的嵌入式系统,操作系统内核

问题3:对整型变量a使用&a会得到什么?为什么只记录其 4 个字节中地址较小的那个地址就够了?

答:

会得到a的地址。只要知道了第一个字节地址,顺藤摸瓜访问到 4 个字节的数据也是可行的。

问题4:指针变量保存的是什么?NULL指针与未初始化的局部指针分别表示什么,又有什么使用风险?

答:

存放地址值。NULL是 C 语言中定义的一个标识符常量,值是 0。0 也是地址,这个地址是无法使用的,读写该地址会报错。NULL指针是初始化为0的指针,后期可以填上新地址,未初始化的指针是野指针,强制使用野指针会导致非法访问内存,程序崩溃。

问题5:声明int *pa时,*int分别说明什么?它与表达式*pa中的解引用操作符有什么区别?

答:

在声明int *pa中,*表明pa是指针变量,int表明它指向的是int类型的对象。这里的*属于声明的一部分,只说明变量类型,不执行计算。在表达式*pa中,*是解引用操作符,表示使用pa保存的地址找到所指对象,从而读取或修改该对象。

问题6:*pa = 0为什么能够修改a?这条语句体现了地址、指针变量和被指向对象怎样的联系?

答:

*pa的意思就是通过pa中存放的地址,找到pa指向的空间;*pa其实就是a变量了。所以,*pa = 0这个操作是把a改成了 0。

问题7:指针变量的大小由什么决定?为什么不同指针类型在同一平台下大小相同,而 X86 与 X64 的测试结果不同?

答:

指针变量的大小是取决于数据模型的。指针变量的大小和类型是无关的,只要是指针类型的变量,在相同的平台下,大小都是相同的。文档给出的原因是平台地址位宽不同:X86 环境使用 32 位地址,指针占 4 字节;X64 环境使用 64 位地址,指针占 8 字节。同一平台的各种指针都用于保存该平台的地址,因此大小相同。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/11 2:23:48

MBTI性格测试:解码16型人格的自我探索工具

1. 为什么我们需要了解自己的性格密码&#xff1f;在咖啡厅里&#xff0c;我经常看到这样的场景&#xff1a;一群人围坐在一起&#xff0c;兴奋地讨论着"我是INTJ"、"原来你是ESFP"之类的话题。这种被称为MBTI的性格测试&#xff0c;正在成为现代人认识自我…

作者头像 李华
网站建设 2026/8/11 2:21:58

零基础做一个密码强度检测工具:弱密码一眼识破

一行需求&#xff0c;一个完整的 Python 项目 —— 含 40 项单元测试&#xff0c;全部通过。 引言 在日常开发中&#xff0c;密码强度检测是一个常见但容易被忽视的功能。无论是用户注册、密码修改还是安全审计&#xff0c;一个可靠的密码强度评估工具能有效提升系统安全性。传…

作者头像 李华
网站建设 2026/8/11 2:21:35

Linux文件I/O操作与重定向机制详解

1. Linux文件I/O的核心地位与学习价值在Linux系统编程中&#xff0c;文件I/O操作就像城市的地下管网系统——虽然普通用户看不见&#xff0c;但支撑着所有应用的数据流动。从最简单的cat命令到复杂的数据库系统&#xff0c;底层都依赖文件描述符&#xff08;file descriptor&am…

作者头像 李华
网站建设 2026/8/11 2:20:28

直播电商团队人才梯队建设与管理的核心策略

1. 直播团队人才梯队建设的核心价值 直播电商行业从2020年开始爆发式增长&#xff0c;到2023年市场规模已突破4.9万亿元。在这个快速发展的赛道中&#xff0c;我见过太多团队因为人才结构不合理而错失机会。一个典型的反面案例是去年某服装品牌的直播团队——他们重金挖来了行业…

作者头像 李华
网站建设 2026/8/11 2:18:09

CSS炸弹攻击实战:HTML邮件无JS键盘记录器漏洞复现与防御

摘要 Black‑Hat USA 2026公开的CSS炸弹攻击&#xff0c;不依赖JavaScript&#xff0c;仅通过HTMLCSS突破网页邮箱的邮件隔离边界&#xff0c;打开恶意邮件即可完成键盘记录、账号密码与会话令牌窃取。传统垃圾邮件过滤、JS拦截、杀毒软件全部失效。本文从底层原理、攻击链路、…

作者头像 李华