
1. 内存和地址摘要本文系统讲解了C语言中指针的核心概念与应用。首先从计算机内存管理基础入手解释了内存单元、地址与虚拟地址空间的关系。接着详细介绍了指针变量的创建、初始化、解引用操作以及指针类型对内存访问的影响。文章深入探讨了指针运算的三种形式指针±整数、指针-指针、关系运算并通过数组访问实例展示指针的实际应用。最后分析了传值调用与传址调用的本质区别以及二级指针的概念与使用场景。全文结合丰富的图示和代码示例帮助读者建立完整的指针知识体系。我们知道计算机上CPU中央处理器在处理数据的时候需要的数据是在内存中读取的处理后的数据也会放回内存中。常见的⼀台电脑上内存是 8GB/16GB/32GB 等那这些内存空间如何⾼效的管理呢我们把内存划分为⼀个个的内存单元每个内存单元的⼤⼩取1个字节然后给每⼀个内存单元⼀个编号就类似于房间编号。注意上⾯我们是为了⽅便讲解对真实的内存访问进⾏了简化的实际上我们编写的运⾏在X86架构的计算机上的C语⾔程序是不会直接访问物理内存的。程序运⾏时为变量分配的地址在程序调试过程中我们看到的地址都是虚拟地址虚拟地址不是物理地址。虚拟地址是虚拟地址空间中的地址。虚拟地址空间可以看作操作系统为每个程序提供的⼀个“假想的、独占的、连续的内存空间”。程序中的虚拟地址会由CPU的内存管理单元(MMU)映射到物理内存中其实程序最终还是会访问物理内存的。这个由虚拟地址换成物理地址的过程暂时我们不需要关⼼学习到操作系统的知识慢慢再理解就可以了。2. 指针变量和地址2.1 取地址操作符理解了内存和地址的关系我们再回到C语⾔在C语⾔中创建变量其实就是向内存申请空间⽐如2.2 指针变量2.2.1 指针变量和初始化那我们通过取地址操作符()拿到的地址是⼀个数值⽐如 0x006FFD70 这个数值有时候也是需要存储起来⽅便后期再使⽤的。这里我们创建一个变量pa,存放a的地址。pa是用来存放地址的变量而地址又叫指针所以该变量又叫指针变量。a的类型是int ,而pa的类型是int * pa。有时候我们创建了⼀个指针变量但是暂时不需要指向变量可以使⽤ NULL 来初始化 NULL 是C语⾔中定义的⼀个标识符常量值是00也是地址这个地址是⽆法使⽤的读写该地址会报错。⼀个指针变量被初始化为 NULL 意思就是这个指针变量暂时不指向任何有效的内存位置等后续使⽤的时候可以给赋值新的地址。2.2.3 解引⽤操作符在现实⽣活中我们使⽤地址要找到⼀个房间在房间⾥可以拿去或者存放物品。程序中我们将地址保存起来未来是要使⽤的那怎么使⽤呢C语⾔中其实也是⼀样的我们只要拿到了地址指针就可以通过地址指针找到地址指针指向的对象这⾥必须学习⼀个操作符叫解引⽤操作符( * )。2.3 指针变量的⼤⼩C语⾔标准并未规定指针变量的⻓度我们现在分别在VS2026的X86和X64环境下测试其实指针变量的⼤⼩是取决于数据模型的下⾯列出了常⻅的4个数据模型数据模型指的是在特定操作系统、CPU 架构和 ABI应⽤程序⼆进制接⼝下C/C 等语⾔中基本整数类型 short 、 int 、 long 、 long long 以及指针类型的具体位宽字节数 的约定。指针变量是用来存放地址的地址的存放需要多大空间指针变量长度就多大3. 指针类型的意义指针变量的⼤⼩和类型⽆关只要是指针变量在同⼀个平台下⼤⼩都是⼀样的为什么还要有各种各样的指针类型呢我们可以看出 char* 类型的指针变量 1 跳过 1 个字节 int* 类型的指针变量 1 跳过了 4个字节。这就是指针变量的类型差异带来的变化。指针 1 其实跳过 1 个指针指向的元素所以指针 n 就是跳过 n 个指针指向的元素。指针可以 n 那也可以 -n 。结论指针的类型决定了指针向前或者向后⾛⼀步⾛多⼤距离。指针±整数让指针可以在内存中向前或者向后移动这就为使⽤指针访问内存打好了基础。3.3 void* 指针在指针类型中有⼀种特殊的类型是 void * 类型的可以理解为⽆具体类型的指针或者叫泛型指针这种类型的指针可以⽤来接受任意类型地址。但是也有局限性void类型的指针不能直接进⾏指针的±整数和解引⽤的运算。*void* pa 无具体类型所以解引用不知道访问几个字节。真的要使用可以这样((int)pa) 100;⼀般 void* 类型的指针是使⽤在函数参数的部分⽤来接收不同类型数据的地址这样的设计可以实现泛型编程的效果。使得⼀个函数来处理多种类型的数据在后期的课程中我们会再次讲解。这里我们重申指针学习的目的用指针来管理内容1 读取内存数据2 数据写入内存指针运算指针的基本运算有三种分别是指针± 整数指针-指针指针的关系运算4.1 指针± 整数4.1.1 指针访问⼀维数组因为数组在内存中是连续存放的只要知道第⼀个元素的地址顺藤摸⽠就能找到后⾯的所有元素。4.2 指针 - 指针4.2.1 为什么会有指针-指针⽇期1 天数 ⽇期2⽇期2 天数 ⽇期1⽇期2 - ⽇期1 天数和⽇期运算的道理⼀样指针也可以进⾏类似的运算指针1 整数n 指针2 (向后跳过n个元素后的地址)指针2 - 整数n 指针1 向前跳过n个元素后的地址)指针2 - 指针1 整数n (指针1 - 指针2 -n)指针 - 指针 结果的绝对值是两个指针之间元素的个数。两个指针相减运算的前提是两个指针指向同⼀块内存空间否则计算没有意义。4.3 指针的关系运算地址的本质还是数值数值是可以⽐较⼤⼩的所以也就满⾜关系运算。这次我们重新改造⼀下前⾯写过的指针访问⼀维数组的代码因为数组的元素的地址随着下标的增⻓在不断地变⼤的所以 arr[0] arr[sz] 的所以我们就可以使⽤指针的关系运算来换⼀种⽅式来实现。5. 传值调⽤和传址调用学习指针的⽬的是使⽤指针解决问题那什么问题⾮指针不可呢我们发现在main函数内部创建了a和ba的地址是0x00cffdd0b的地址是0x00cffdc4在调⽤Swap1函数时将a和b传递给了Swap1函数在Swap1函数内部创建了形参x和y接收a和b的值但是x的地址是0x00cffcecy的地址是0x00cffcf0x和y确实接收到了a和b的值不过x的地址和a的地址不⼀样y的地址和b的地址不⼀样相当于x和y是独⽴的空间那么在Swap1函数内部交换x和y的值⾃然不会影响a和b当Swap1函数调⽤结束后回到main函数a和b的没法交换。Swap1函数在使⽤的时候是把变量本⾝直接传递给了函数这种调⽤函数的⽅式叫传值调⽤。我们在学习函数时编写的函数基本都是传值调⽤。那怎么办呢我们现在要解决的就是当调⽤Swap函数的时候Swap函数内部操作的就是main函数中的a和b直接将a和b的值交换了。那么就可以使⽤指针了在main函数中将a和b的地址传递给Swap函数Swap函数⾥边通过地址间接的操作main函数中的a和b并达到交换的效果就好了。6. ⼆级指针指针变量也是变量是变量就有地址那指针变量的地址存放在哪⾥这就是 ⼆级指针 。