C语言指针:数组名、二级指针
文章目录一、数组名的含义1. 数组名的理解2. 指针访问数组二、一维数组传参的本质三、二级指针⭐⭐⭐1. 二级指针的含义2. 二级指针的运算一、数组名的含义1. 数组名的理解(1)数组名表示数组首元素的地址我们来做个测试下面有一段代码#includestdio.hintmain(){intarr[10]{1,2,3,4,5,6,7,8,9,10};printf(arr[0] %p\n,arr[0]);printf(arr %p\n,arr);return0;}程序运行结果通过上面代码的运行结果可以知道arr[0]和arr打印的结果一样说明数组名表示的是数组第一个元素的地址。(2) 虽然数组名表示的是数组首元素的地址但是有两个特例sizeof(数组名)和数组名。看下面一段代码#includestdio.hintmain(){intarr[10]{1,2,3,4,5,6,7,8,9,10};printf(%d\n,sizeof(arr));return0;}输出结果由上图输出结果为40既然数组名表示的是数组首元素的地址那输出的结果应该是4/8(看平台)那为什么这里是40呢数组名确实表示数组首元素的地址但是一但数组名单独放在sizeof这个操作符里面那计算的就是整个数组的大小(单位字节)。上面的整型数组有10个元素所以整个数组的大小就是40个字节。再看一段代码#includestdio.hintmain(){intarr[10]{1,2,3,4,5,6,7,8,9,10};printf(arr %p\n,arr);printf(arr %p\n,arr);return0;}运行结果由上图数组名本来就表示数组首元素的地址再对数组名取地址得到的地址和数组首元素的地址是一样的。既然数组名和数组名都是数组中第一个元素的地址那他们有什么区别吗看下面的程序#includestdio.hintmain(){intarr[10]{1,2,3,4,5,6,7,8,9,10};printf(arr %p\n,arr);printf(arr1 %p\n,arr1);printf(arr %p\n,arr);printf(arr1 %p\n,arr1);return0;}运行结果由上图虽然数组名和数组名的地址都一样。但是数组名表示的是首元素的地址而数组名表示的是整个数组的地址。通过上面指针的运算就能深刻的体会到为什么数组名表示的是整个数组的地址。可以看到上面代码运行的结果arr1就直接加了40个字节的大小直接跳过了整个数组。(上面的地址是十六进制的所以004FFC44加40(十进制的40转化为16进制的数等于28)就等于004FFC6C)。总结●sizeof(数组名)sizeof中单独放数组名时那数组名表示的就是整个数组计算的是整个数组的大小单位是字节。●数组名对数组名取地址那数组名就表示整个数组取出的是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的)。除上面两个特例外其他任何地方使用数组名数组名都表示数组首元素的地址。(3) 我们再通过下面一段代码深刻体会上面的知识点#includestdio.hintmain(){intarr[10]{1,2,3,4,5,6,7,8,9,10};printf(arr[0] %p\n,arr[0]);printf(arr[0]1 %p\n,arr[0]1);printf(arr %p\n,arr);printf(arr1 %p\n,arr1);printf(arr %p\n,arr);printf(arr1 %p\n,arr1);return0;}运行结果由上图可以发现arr[0]、arr和arr三个的地址都是一样的而它们加1后得到的结果不都是相同的。arr[0]1和arr[0]相差4个字节arr1和arr相差4个字节1都表示跳过一个元素说明arr[0]和arr是等价的都表示数组首元素的地址。而arr1的结果跟前面两个加1的结果不一样arr1与arr相差40个字节所以arr表示的是整个数组的地址。2. 指针访问数组前面了解到数组名表示数组首元素的地址那么我们就可以通过指针来访问数组#includestdio.hintmain(){intarr[10]{0};intszsizeof(arr)/sizeof(arr[0]);int*parr;inti0;for(i0;isz;i){scanf(%d,pi);}//打印数组元素for(i0;isz;i){printf(%d ,*(pi));}return0;}调试结果上面的代码中数组名arr是数组首元素的地址将数组名arr赋值给了指针变量p那么数组名arr和p就是等价的。我们通过arr[i]可以访问数组的元素那通过p[i]是否也可以访问数组的元素呢#includestdio.hintmain(){intarr[10]{0};intszsizeof(arr)/sizeof(arr[0]);int*parr;inti0;for(i0;isz;i){scanf(%d,pi);}for(i0;isz;i){printf(%d ,p[i]);}return0;}运行结果通过上面的代码和运行结果可知将*(pi)换成p[i]也可以访问数组元素。说明p[i]和*(pi)是等价的。同理arr[i]和*(arri)也是等价的。数组元素的访问在编译器处理的时候也是转换成首元素的地址偏移量求出元素的地址然后解引用来访问的。二、一维数组传参的本质数组是可以作为参数传递给函数的那数组传参的本质是什么看下面的代码#includestdio.hvoidtest(intarr[]){intsz2sizeof(arr)/sizeof(arr[0]);printf(sz2 %d\n,sz2);}intmain(){intarr[10]{1,2,3,4,5,6,7,8,9,10};intsz1sizeof(arr)/sizeof(arr[0]);printf(sz1 %d\n,sz1);test(arr);return0;}运行结果之前讲过求数组元素个数的方法但现在是在函数内部求数组元素个数通过上面的运行结果可知在函数外部求数组元素个数打印的结果确实是数组元素的个数但在函数内部求数组元素个数时发现没有正确打印数组元素的个数。原因是什么呢由于数组名表示数组首元素的地址数组传参的时候传递的是数组名。所以数组传参本质上传递的是数组首元素的地址。所以函数形参的部分理论上应该使用指针变量来接收首元素的地址。那么在函数内部我们写sizeof(arr)计算的就是一个地址的大小(单位字节)而不是数组的大小。正因为函数的参数部分本质是指针所以传递数组名给函数在函数内部是没办法求数组元素个数的。看下面一段代码#includestdio.h//参数写成数组的形式本质上还是指针voidtest1(intarr[]){printf(%d\n,sizeof(arr));}//参数写成指针的形式voidtest2(int*arr){//计算一个指针变量的大小printf(%d\n,sizeof(arr));}intmain(){intarr[10]{1,2,3,4,5,6,7,8,9,10};test1(arr);test2(arr);return0;}运行结果由上面代码的运行结果可知数组传参形参部分不管是写成数组形式还是写成指针形式在函数内部通过sizeof(arr)计算的都是地址的大小所以调用两个函数打印的都是4个字节(32位平台)。还有就是数组传参写成数组的形式数组的元素个数可写可不写总结一维数组传参形参的部分可以写成数组的形式也可以写成指针的形式。三、二级指针⭐⭐⭐1. 二级指针的含义通过指针变量能存储其他变量的地址而且指针变量也是有自己的地址的那指针变量的地址存放在哪里呢答案是通过二级指针存放。即指向一级指针的指针就叫作二级指针。来看一段代码#includestdio.hintmain(){inta10;int*paa;int**ppapa;printf(a %d\n,a);printf(pa %p\n,pa);printf(ppa %p\n,ppa);printf(ppa %p\n,ppa);return0;}运行结果图文解释由上图指针pa存储了整型变量a的地址所以pa指向了a指针pa称为一级指针而指针ppa又存储了指针pa的地址所以ppa指向了pa指针ppa称为二级指针同理还有三级指针…上图中二级指针变量ppa的地址0x008FFA28就需要用三级指针来存储。(注我们通常把指针变量简称为指针而单独说哪个变量的指针的时候那指针就是地址)2. 二级指针的运算●*ppa通过对指针变量ppa中的地址进行解引用就找到了pa*ppa其实访问的就是pa。inta10;intb20;int*paa;int**ppapa;*ppab;//*ppa就等价于pa●**ppa先通过*ppa找到pa然后再对pa进行解引用*pa那就找到了a。inta10;int*paa;int**ppapa;**ppa30;//等价于 *pa 30 等价于 a 30对于上面的二级指针ppa的解引用运算的总结*ppa pa**ppa *pa a即二级指针解引用一次得到的就是指向的一级指针二级指针解引用两次得到的就是一级指针指向的对象。