指针核心知识(上) 📅 2026/8/11 2:25:43 第 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 阅读前先看这几个问题文档怎样从“房间号”类比推导出内存单元、地址和指针的关系CPU 读写数据时虚拟地址、MMU 和物理内存之间是什么关系哪些特殊领域可能直接使用物理地址对整型变量a使用a会得到什么为什么只记录其 4 个字节中地址较小的那个地址就够了指针变量保存的是什么NULL指针与未初始化的局部指针分别表示什么又有什么使用风险声明int *pa时*与int分别说明什么它与表达式*pa中的解引用操作符有什么区别*pa 0为什么能够修改a这条语句体现了地址、指针变量和被指向对象怎样的联系指针变量的大小由什么决定为什么不同指针类型在同一平台下大小相同而 X86 与 X64 的测试结果不同1.2 读完后完成这 3 个任务1.2.1 任务 1梳理内存、地址与指针做什么画出“内存单元—地址—指针—CPU”的关系并在图中补上虚拟地址经 MMU 映射到物理内存的方向。验证标准能说明一个内存单元为 1 个字节、一个字节含 8 个比特位并能区分程序调试时看到的虚拟地址与物理地址。1.2.2 任务 2运行取址与解引用示例做什么运行文档中的取地址和解引用示例观察a、pa与a的值再执行*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. 目标理解指针的本质就是地址。理解指针类型的意义能够使用正确的指针类型。理解指针运算的本质就是地址的偏移。掌握传值调用和传址调用的区别。掌握二级指针。为后续指针进阶打好基础。3. 内存和地址学习指针必须先理解内存和地址。在讲解之前先看一个生活中的案例假设有一栋宿舍楼把你放在楼里楼上有 100 个房间但是房间没有编号。你的一个朋友来找你玩如果想找到你就得挨个房子去找这样效率很低。但是如果我们根据楼层和楼层的房间情况给每个房间编上号如一楼101102103... 二楼201202203... ...有了房间号如果你的朋友得到房间号就可以快速地找到房间找到你。生活中每个房间有了房间号就能提高效率能快速地找到房间。如果把上面的例子对照到计算机中又是怎么样呢我们知道计算机上 CPU中央处理器在处理数据的时候需要的数据是在内存中读取的处理后的数据也会放回内存中。常见的一台电脑上内存是 8 GB/16 GB/32 GB 等那这些内存空间如何高效地管理呢解决办法和上面的例子相似把内存划分为一个个的内存单元每个内存单元的大小取 1 个字节然后给每一个内存单元一个编号就类似于房间编号。计算机中常见的单位介绍一个比特位可以存储一个二进制的位 1 或者 0。bit - 比特位 Byte - 字节 KB MB GB TB PB1Byte 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 环境#includestdio.hintmain(){inta10;return0;}比如上述的代码就是创建了整型变量a在内存中申请 4 个字节用于存放整数 10其中每个字节都有地址。上图中 4 个字节的地址分别是0x006FFD70 0x006FFD71 0x006FFD72 0x006FFD73那我们如何能得到a的地址呢这里就得学习一个操作符——取地址操作符。X86 环境#includestdio.hintmain(){inta10;a;//取出a的地址printf(%p\n,a);return0;}按照我画图的例子会打印出来006FFD70。a取出的是a所占 4 个字节中地址较小的字节的地址。虽然整型变量占用 4 个字节我们只要知道了第一个字节地址顺藤摸瓜访问到 4 个字节的数据也是可行的。4.2 指针变量4.2.1 指针变量和初始化我们通过取地址操作符拿到的地址是一个数值比如0x006FFD70。这个数值有时候也需要存储起来方便后期再使用。那我们把这样的地址值存放在哪里呢答案是指针变量中。比如#includestdio.hintmain(){inta10;int*paa;//取出a的地址并存储到指针变量pa中return0;} 指针变量也是一种变量这种变量就是用来存放地址的存放在指针变量中的值都会理解为地址。这里我们是拿a也就是a的地址给pa进行了初始化。有时候我们创建了一个指针变量但是暂时不需要指向变量可以使用NULL来初始化。NULL是 C 语言中定义的一个标识符常量值是 0。0 也是地址这个地址是无法使用的读写该地址会报错。#ifdef__cplusplus#defineNULL0#else#defineNULL((void*)0)#endif一个指针变量被初始化为NULL意思就是这个指针变量暂时不指向任何有效的内存位置。等后续使用的时候可以给它赋值新的地址。使用NULL初始化指针变量intmain(){int*paNULL;return0;}❓ 那如果一个指针变量没有初始化会怎样呢指针变量如果是全局的则默认会被初始化为 0也就是NULL指针。指针变量如果是局部的则指针变量的值是未知的这样的指针就是野指针强制使用野指针会导致非法访问内存程序崩溃。4.2.2 如何拆解指针类型我们看到pa的类型是int*该如何理解指针的类型呢inta10;int*paa;这里pa左边写的是int*int*是pa的类型但是*和int应该分开理解。*是在说明pa是指针变量而前面的int是在说明pa指向的是整型int类型的对象。那如果有一个char类型的变量chch的地址要放在什么类型的指针变量中呢charchw;pcch;//pc的类型怎么写呢4.2.3 解引用操作符在现实生活中我们使用地址要找到一个房间在房间里可以拿取或者存放物品。程序中我们将地址保存起来未来是要使用的那怎么使用呢C 语言中其实也是一样的。我们只要拿到了地址指针就可以通过地址指针找到地址指针指向的对象。这里必须学习一个操作符叫解引用操作符*。#includestdio.hintmain(){inta100;int*paa;*pa0;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 环境下测试#includestdio.hintmain(){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 等语言中基本整数类型short、int、long、long long以及指针类型的具体位宽字节数的约定。数据模型intlonglong longpointer指针操作系统LP3216 位32 位64 位32 位Win16 APIILP3232 位32 位64 位32 位Win32 APIUnix 及类 Unix 系统Linux、Mac OS XLLP6432 位32 位64 位64 位Win64 APILP6432 位64 位64 位64 位Unix 与类 Unix 系统Linux、Mac OS X结论32 位平台下地址是 32 个 bit 位指针变量大小是 4 个字节。64 位平台下地址是 64 个 bit 位指针变量大小是 8 个字节。注意指针变量的大小和类型是无关的只要是指针类型的变量在相同的平台下大小都是相同的。5.回答问题卡问题问题1:文档怎样从“房间号”类比推导出内存单元、地址和指针的关系答一个内存单元类似于一个独立房间内存地址就是房间门牌号指针变量就是是专门保存地址值的变量代表存放房间号的记事本指针本质上是地址。每个内存单元为 1 个字节一个字节包含 8 个比特位。问题2CPU 读写数据时虚拟地址、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 字节。同一平台的各种指针都用于保存该平台的地址因此大小相同。