C语言指针深度剖析从内存地址到传址调用指针是C语言的精髓也是许多初学者觉得难以掌握的部分。本文将系统讲解内存与地址的关系、指针变量的定义与使用、指针类型的意义、const修饰指针、指针运算、野指针的成因与规避、assert断言的使用最后通过模拟strlen和交换函数深刻理解传值调用与传址调用的区别。目录一、内存和地址二、指针变量和地址三、指针变量类型的意义四、const修饰指针五、指针运算六、野指针七、assert断言八、指针的使用和传址调用一、内存和地址计算机中的内存被划分为一个个内存单元每个单元大小为1字节。每个内存单元都有一个唯一的编号这个编号就是地址在C语言中也称为指针。32位机器有32根地址总线可产生2 32 2^{32}232种地址每个地址占4字节。64位机器有64根地址总线每个地址占8字节。内存单元的编号 地址 指针二、指针变量和地址2.1 取地址操作符创建变量时系统为其分配内存空间。使用可以获取变量的起始地址。inta10;printf(%p\n,a);// 打印a的地址第一个字节地址2.2 指针变量和解引用操作符*指针变量是专门用来存放地址的变量。inta10;int*paa;// pa是指针变量类型为int*解引用操作符*通过地址访问其指向的对象*pa20;// 等价于 a 202.3 指针变量的大小32位平台指针变量占4字节64位平台指针变量占8字节指针变量的大小与类型无关char*、int*等大小相同。三、指针变量类型的意义指针类型决定了解引用操作的权限一次能访问几个字节指针±整数的步长3.1 解引用权限intn0x11223344;int*pin;*pi0;// 将n的4个字节全部改为0char*pc(char*)n;*pc0;// 只将n的第一个字节改为03.2 指针±整数的步长int*pin;char*pc(char*)n;printf(%p\n,pi);// 假设 0x1000printf(%p\n,pi1);// 0x1004跳4字节printf(%p\n,pc1);// 0x1001跳1字节3.3void*类型void*是泛型指针可以接收任意类型地址但不能直接进行解引用和指针运算常用于函数参数实现泛型编程。四、const修饰指针4.1 const修饰变量const修饰的变量在语法上不能被直接修改constintn0;n20;// 编译错误4.2 const修饰指针变量写法含义const int *pconst在*左边指针指向的内容不能通过p修改但p本身可以指向其他变量。int *const pconst在*右边p本身的值不能修改不能指向别处但可以通过p修改指向的内容。const int *const p指针本身和指向的内容都不能修改。五、指针运算5.1 指针 ± 整数利用数组连续存储的特性通过指针遍历数组intarr[10]{0};int*parr[0];for(inti0;i10;i){*(pi)i;// pi 跳过i个元素}5.2 指针 - 指针两个指针相减得到它们之间元素的个数前提指向同一数组。intmy_strlen(char*s){char*ps;while(*p!\0)p;returnp-s;// 指针之差 字符个数}5.3 指针的关系运算可以比较指向同一数组的两个指针的大小地址高低。while(parrsz){...}六、野指针野指针指向不可知的地址使用它会引发不可预料的错误。6.1 野指针的成因指针未初始化局部指针变量默认随机值。指针越界访问超出数组范围。指针指向的空间被释放如返回局部变量的地址。6.2 如何规避野指针初始化指针明确指向时赋值地址否则置为NULL。小心指针越界。不再使用时及时置为NULL使用前检查有效性。不要返回局部变量的地址。int*pNULL;// 安全初始化// ...if(p!NULL){// 使用p}七、assert断言assert是定义在assert.h中的宏用于在运行时检查条件是否满足。assert(p!NULL);如果条件为假程序终止并输出错误信息文件名、行号。可以在#include assert.h之前定义#define NDEBUG来禁用所有断言通常用于Release版本。断言帮助程序员快速定位逻辑错误是调试的有力工具。八、指针的使用和传址调用8.1 模拟实现strlenintmy_strlen(constchar*str){intcount0;assert(str!NULL);while(*str){count;str;}returncount;}8.2 传值调用 vs 传址调用经典问题交换两个整数的值。传值调用将变量本身传给函数形参是实参的临时拷贝函数内修改形参不影响实参。voidSwap1(intx,inty){inttmpx;xy;ytmp;// 只交换了形参}// 调用Swap1(a, b) 无法交换a和b传址调用将变量的地址传给函数通过指针间接操作实参。voidSwap2(int*px,int*py){inttmp*px;*px*py;*pytmp;}// 调用Swap2(a, b) 成功交换a和b结论若函数只需要使用实参的值采用传值调用。若函数需要修改实参必须采用传址调用。总结指针是C语言访问内存的直接途径。理解内存与地址、掌握指针变量的定义与运算、区分指针类型意义、正确使用const和assert、避免野指针并能够根据需求选择传值或传址调用是学好C语言的关键。后续深入指针(2)(3)(4)将探讨更高级的指针用法如二级指针、函数指针、指针数组等。