C语言指针深入理解计算机内存一、指针的基本概念1.1 什么是指针指针是C语言的灵魂也是C语言区别于其他高级语言的重要特征。要理解指针首先需要理解计算机内存的概念。计算机中的每个变量都存储在内存中而内存中的每个位置都有一个地址。指针就是存储变量地址的变量。inta10;在内存中这行代码会分配一个整型变量的空间将值10存入这个空间这个空间有一个地址比如0x0061FF2C而指针变量就是专门用来存储这些地址的变量。1.2 指针的定义定义指针变量的语法如下类型名*指针变量名;int*p;// 指向整型数据的指针float*pf;// 指向浮点型数据的指针char*pc;// 指向字符型数据的指针注意指针变量只能指向特定类型的变量1.3 取地址运算符与解引用运算符#includestdio.hintmain(){inta10;int*p;// 定义指针变量ppa;// 是取地址运算符将a的地址赋给pprintf(a的值%d\n,a);// 输出10printf(a的地址%p\n,a);// 输出0x0061FF2C示例printf(p的值%p\n,p);// 输出0x0061FF2C与a的地址相同printf(*p的值%d\n,*p);// 输出10解引用获取p指向的内容return0;}重要概念取地址运算符获取变量的地址*解引用运算符访问指针指向的变量1.4 指针的大小指针变量本身也是一种变量它占用一定的内存空间。在32位系统中指针占用4个字节在64位系统中指针占用8个字节。#includestdio.hintmain(){printf(int * 大小%zu字节\n,sizeof(int*));printf(char * 大小%zu字节\n,sizeof(char*));printf(double * 大小%zu字节\n,sizeof(double*));return0;}二、指针的初始化2.1 初始化为NULL可以将指针初始化为NULL表示它不指向任何有效地址int*pNULL;// 等价于 int *p; p NULL;2.2 初始化为有效地址inta10;int*pa;// 指向变量aint*p2p;// 指向p指向的地址指向a2.3 野指针野指针是指向未知地址的指针使用野指针可能导致程序崩溃int*p;// 未初始化的指针其值是随机的*p10;// 危险可能修改了未知内存导致程序崩溃避免野指针的方法初始化为NULL指向合法内存后使用使用完指针后设为NULL三、指针运算3.1 指针与整数的加减指针可以与整数进行加减运算表示向前或向后移动n个元素#includestdio.hintmain(){intarr[]{10,20,30,40,50};int*parr;// 数组名就是首元素地址printf(*p %d\n,*p);// 输出10p指向第一个元素pp1;// 向后移动一个int的位置printf(*p %d\n,*p);// 输出20pp2;printf(*p %d\n,*p);// 输出50return0;}3.2 指针减去指针两个相同类型的指针可以相减结果是两个指针之间的元素个数#includestdio.hintmain(){intarr[]{10,20,30,40,50};int*p1arr[0];int*p2arr[4];printf(p2 - p1 %ld\n,p2-p1);// 输出4return0;}3.3 指针的关系运算指针可以进行比较运算intarr[5]{1,2,3,4,5};int*parr;while(parr5)// 循环遍历数组{printf(%d ,*p);p;}四、指针与数组4.1 数组与指针的关系在C语言中数组名就是数组首元素的地址intarr[5]{1,2,3,4,5};int*parr;// 等价于 int *p arr[0];因此可以用指针来访问数组元素#includestdio.hintmain(){intarr[]{10,20,30,40,50};int*parr;// 方式1使用下标for(inti0;i5;i)printf(%d ,arr[i]);printf(\n);// 方式2使用指针for(inti0;i5;i)printf(%d ,*(pi));printf(\n);// 方式3指针递增for(int*qarr;qarr5;q)printf(%d ,*q);printf(\n);return0;}4.2 指针与数组的重要等价关系arr[i]*(arri)*(pi)p[i]注意虽然数组名可以当作指针使用但两者有区别数组名是常量指针不能被赋值指针是变量可以被赋值4.3 数组作为函数参数当数组作为函数参数时实际上传递的是数组的首地址#includestdio.hvoidprintArray(int*arr,intn)// 等价于 int arr[]{for(inti0;in;i)printf(%d ,arr[i]);printf(\n);}intmain(){intarr[]{1,2,3,4,5};printArray(arr,5);// arr就是数组首地址return0;}五、字符指针与字符串5.1 字符指针字符指针可以指向一个字符charcA;char*pcc;5.2 字符串与字符指针字符串常量本身就是一个地址可以直接赋值给字符指针char*strHello;注意这里的Hello存储在常量区指针str指向字符串的首字符’H’。#includestdio.hintmain(){char*strHello;printf(%s\n,str);// 输出整个字符串printf(%c\n,*str);// 输出Hprintf(%c\n,*(str1));// 输出ereturn0;}六、指针作为函数参数6.1 通过指针交换两个变量的值由于值传递无法直接修改实参可以通过指针实现#includestdio.hvoidswap(int*x,int*y){inttemp*x;*x*y;*ytemp;}intmain(){inta10,b20;printf(交换前a %d, b %d\n,a,b);swap(a,b);// 传入地址printf(交换后a %d, b %d\n,a,b);return0;}// 输出// 交换前a 10, b 20// 交换后a 20, b 106.2 返回指针的函数函数可以返回指针但要注意返回的指针必须指向合法内存#includestdio.hint*findMax(int*arr,intn){int*pmaxarr;for(inti1;in;i){if(*(arri)*pmax)pmaxarri;}returnpmax;}intmain(){intarr[]{3,7,2,9,1};int*maxPtrfindMax(arr,5);printf(最大值%d\n,*maxPtr);return0;}七、指针数组与数组指针7.1 指针数组指针数组是数组其元素是指针int*arr[5];// 数组包含5个int*类型的元素应用场景存储多个字符串#includestdio.hintmain(){char*names[]{Alice,Bob,Charlie,David};intnsizeof(names)/sizeof(names[0]);for(inti0;in;i)printf(%s\n,names[i]);return0;}7.2 数组指针数组指针是指向数组的指针int(*p)[5];// 指针指向包含5个int元素的数组#includestdio.hintmain(){intarr[3][4]{{1,2,3,4},{5,6,7,8},{9,10,11,12}};int(*p)[4]arr;// p指向二维数组的一行// 访问元素printf(%d\n,*(*(p1)2));// 输出7等价于arr[1][2]return0;}八、void指针void *表示通用指针可以指向任意类型void*pv;// void指针inta10;charbA;pva;// 指向intpvb;// 指向char注意void指针不能直接解引用需要先转换为具体类型的指针。九、本章小结本章我们详细介绍了C语言的指针指针的基本概念存储变量地址的变量指针的定义与初始化不同类型指针的写法指针运算加减运算、关系运算指针与数组数组名就是首地址字符指针与字符串字符串常量的使用指针作为函数参数实现地址传递指针数组与数组指针两者的区别void指针通用指针指针是C语言的核心概念也是C语言强大功能的来源。掌握指针可以让程序直接操作内存实现更高效、更灵活的代码。下一章预告我们将学习C语言的结构体与联合这是构建更复杂数据类型的基础。