相关阅读C语言https://blog.csdn.net/weixin_45791458/category_12423166.html?spm1001.2014.3001.5482在C语言中指向数组的指针和指向数组首元素的指针经常被混淆很多时候甚至会被笼统地统称为“数组指针”。但实际上这两者并不是一回事它们的类型不同、参与运算时的含义不同、使用方式也不同。本文就围绕这一点展开讨论。下面先定义一个数组然后分别定义指向该数组首元素的指针和指向整个数组的指针。int arr[] {0, 1, 2}; int *ptr0 arr; // 创建一个int型指针指针值为数组首元素地址 int *ptr1 arr[0]; // 创建一个int型指针指针值为数组首元素地址 int (*ptr2)[3] arr; // 创建一个指向整个数组的指针指针值为整个数组的地址乍一看这三个指针对应的地址值似乎是一样的也就是说在数值上常常会看到arr arr[0] arr。这看起来有点奇怪因为它们明明不是同一种东西为什么地址值却一样原因在于它们表示的是同一块内存的起始位置但类型不同因此编译器对它们的解释方式不同。arr[0]与arr对于编译器来说没有任何区别因此ptr0和ptr1两个指针的各种性质都是一样的。arr这个常量值虽然与其他两者相同但是对编译器而言它代表了整个数组的地址因此当arr与其他数加减时会将其他数转化为以数组的字节数为单位的地址值对于指针ptr2来说也是如此。//例1 int arr[] {0,1,2}; //下面两条语句等价 int *ptr0 arr; //创建了一个int型指针指针值为数组首元素地址 int *ptr1 arr[0]; //创建了一个int型指针指针值为数组首元素地址 int (*ptr2) [3] arr; //创建了一个int型数组指针指针值为整个数组的地址 printf(arr is %p\n, arr); printf(arr 1 is %p\n, arr 1); printf(arr[0] is %p\n, arr[0]); printf(arr[0] 1 is %p\n, arr[0] 1); printf(arr is %p\n, arr); printf(arr 1 is %p\n, arr 1); printf(ptr0 is %p\n, ptr0); printf(ptr0 1 is %p\n, ptr0 1); printf(ptr1 is %p\n, ptr1); printf(ptr1 1 is %p\n, ptr1 1); printf(ptr2 is %p\n, ptr2); printf(ptr2 1 is %p\n, ptr2 1); 输出 arr is 000000000061FDFC arr 1 is 000000000061FE00 arr[0] is 000000000061FDFC arr[0] 1 is 000000000061FE00 arr is 000000000061FDFC arr 1 is 000000000061FE08 ptr0 is 000000000061FDFC ptr0 1 is 000000000061FE00 ptr1 is 000000000061FDFC ptr1 1 is 000000000061FE00 ptr2 is 000000000061FDFC ptr2 1 is 000000000061FE08从上面的例1可以看到arr、arr[0]、arr、ptr0、ptr1、ptr2的值都是相同的但是arr、arr[0]、ptr0、ptr1加1在编译后对地址值加了4因为int类型占4字节的空间arr、ptr2加1在编译后对地址值加了12因为数组含有三个int类型的数据因此数组占12字节的空间。在一维数组中这种区别还不算特别明显而在多维数组中指向数组的指针就会变得非常常见如例2所示。//例2 int arr[2][2] {{0, 1}, {2, 3}}; //创建一个多维数组 int (*ptr0) [2][2] arr; //创建了一个int型二维数组指针指针值为二维数组的地址 //下面两条语句等价 int (*ptr1) [2] arr; //创建了一个int型一维数组指针指针值为两个数组中第一个数组的地址 int (*ptr2) [2] arr[0]; //创建了一个int型一维数组指针指针值为两个数组中第一个数组的地址 int *ptr3 arr[0]; //创建了一个int型指针指针值为两个数组中第一个数组的首元素地址 int *ptr4 arr[0][0]; //创建了一个int型指针指针值为两个数组中第一个数组的首元素地址 printf(ptr0 is %p\n, ptr0); printf(ptr0 1 is %p\n, ptr0 1); printf(ptr1 is %p\n, arr[0]); printf(ptr1 1 is %p\n, ptr1 1); printf(ptr2 is %p\n, ptr2); printf(ptr2 1 is %p\n, ptr2 1); printf(ptr3 is %p\n, ptr3); printf(ptr3 1 is %p\n, ptr3 1); printf(ptr4 is %p\n, ptr4); printf(ptr4 1 is %p\n, ptr4 1); 输出 ptr0 is 000000000061FDE0 ptr0 1 is 000000000061FDF0 ptr1 is 000000000061FDE0 ptr1 1 is 000000000061FDE8 ptr2 is 000000000061FDE0 ptr2 1 is 000000000061FDE8 ptr3 is 000000000061FDE0 ptr3 1 is 000000000061FDE4 ptr4 is 000000000061FDE0 ptr4 1 is 000000000061FDE4可以看到即使ptr0、ptr1、ptr2、ptr3的值都是一样的ptr01在编译后对地址值加了16二维数组占4个字节空间ptr11和ptr21在编译后对地址值加了8一维数组占8个字节空间ptr31在编译后对地址值加了4int类型占4个字节空间。下面的例3展示了如何使用指向数组的指针和指向数组首元素的指针来访问数组元素。例3 int arr[] {0, 1, 2}; //下面两条语句等价 int *ptr0 arr; //创建了一个int型指针指针值为数组首元素地址 int *ptr1 arr[0]; //创建了一个int型指针指针值为数组首元素地址 int (*ptr2) [3] arr; //创建了一个int型数组指针指针值为整个数组的地址 printf(arr[0] is %d\n, ptr0[0]); //使用指向数组首元素的指针和[]打印0 printf(arr[1] is %d\n, ptr0[1]); //使用指向数组首元素的指针和[]打印1 printf(arr[2] is %d\n, ptr0[2]); //使用指向数组首元素的指针和[]打印2 printf(arr[0] is %d\n, arr[0]); //使用数组名和[]打印0 printf(arr[1] is %d\n, arr[1]); //使用数组名和[]打印1 printf(arr[2] is %d\n, arr[2]); //使用数组名和[]打印2 //一个很傻的例子它是不必要的 printf(arr[0] is %d\n, (arr[0])[0]); //使用首元素地址和[]打印0 printf(arr[1] is %d\n, (arr[0])[1]); //使用首元素地址和[]打印1 printf(arr[2] is %d\n, (arr[0])[2]); //使用首元素地址和[]打印2 //实际上还能更傻这么做的可读性很差 printf(arr[0] is %d\n, (((arr[0])[0]))[0]); //奇怪地打印0 printf(arr[1] is %d\n, (((arr[0])[0]))[1]); //奇怪地打印1 printf(arr[2] is %d\n, (((arr[0])[0]))[2]); //奇怪地打印2 //尽管ptr2的值和ptr1、ptr0一样但仍需先用*解引用才能再使用[]访问元素 printf(arr[0] is %d\n, (*ptr2)[0]); //使用指向数组的指针和[]打印0 printf(arr[1] is %d\n, (*ptr2)[1]); //使用指向数组的指针和[]打印1 printf(arr[2] is %d\n, (*ptr2)[2]); //使用指向数组的指针和[]打印2 printf(arr[0] is %d\n, ptr2[0]); //错误的 printf(arr[1] is %d\n, ptr2[1]); //错误的 printf(arr[2] is %d\n, ptr2[2]); //错误的 printf(arr[0] is %d\n, *(ptr0 0)); //使用指向数组首元素的指针和*打印0 printf(arr[1] is %d\n, *(ptr0 1)); //使用指向数组首元素的指针和*打印1 printf(arr[2] is %d\n, *(ptr0 2)); //使用指向数组首元素的指针和*打印2 printf(arr[0] is %d\n, *(arr 0)); //使用数组名和*打印0 printf(arr[1] is %d\n, *(arr 1)); //使用数组名和*打印1 printf(arr[2] is %d\n, *(arr 2)); //使用数组名和*打印2 //尽管ptr2的值和ptr1、ptr0一样但仍需先用*解引用才能再使用*访问元素 printf(arr[0] is %d\n, *((*ptr2) 0)); //使用指向数组的指针和*打印0 printf(arr[1] is %d\n, *((*ptr2) 1)); //使用指向数组的指针和*打印1 printf(arr[2] is %d\n, *((*ptr2) 2)); //使用指向数组的指针和*打印2 printf(arr[0] is %d\n, *(ptr2 0)); //错误的 printf(arr[1] is %d\n, *(ptr2 1)); //错误的 printf(arr[2] is %d\n, *(ptr2 2)); //错误的读到这里其实最容易混淆的点主要有两个。1、地址值相同不代表类型相同arr、arr[0]、arr在打印出来时很可能数值一样但它们的类型并不一样。而在C语言里类型决定了指针运算的含义。2、指向整个数组的指针访问元素时要先解引用int *可以直接用[]访问元素因为它指向的是元素本身。而int (*)[3]指向的是整个数组所以必须先*一次变成数组再访问其中的元素。这一点在多维数组里尤其重要因为多维数组处理中最常见的其实就是“指向数组的指针”。下面的博客讨论了C语言中字符串字面量的保存位置欢迎阅读。C语言字符串字面量及其保存位置https://chenzhang.blog.csdn.net/article/details/135142133