Part1:
指针数组 用法 低级 写入 输出 正常
在之前,指针数组有提到过,是一个数组,存放指针的数组。为什么会出现这个东西,当我们要把许多地址都交给指针变量的时候,需要一个一个写,太麻烦了,指针数组解了这个问题。来看一下指针数组的使用:和定义普通类型数组一般,只是赋值时的内容变成了地址,类型加上*。
使用的话,我们可以用一个循环来找到每个指针的地址,再解引用它。
#include<stdio.h>
int main(void) {
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int* parr[] = { &a,&b,&c,&d,&e };//输入,类型变为int*,一个纯纯的数组
int i = 0; //输出
for (i = 0; i < 5; i++) {
printf("%d ", *parr[i]);
}
return 0;
}
这样的写法大多于初学者,我们有很多int类型的变量,为什么不写成一个数组呢?指针数组的类型也是统一的,只能存放相同类型的指针变量。我们平时写数组时,知道数组首地址就可以得到整个数组的元素,这里同样如此,我们只需要一个指针变量指向数组首地址即可。多个元素指向多个同类型的数组,把指针放在一起成一个指针数组,一个类似二维数组的结构就出现了
#include<stdio.h>
int main(void) {
int a[] = { 1,2,3,4,5 };
int b[] = { 2,3,4,5,6 };
int c[] = { 3,4,5,6,7 };
int* parr[] = { a,b,c }; //输入
int i = 0;
for (i = 0; i < 3; i++) { //找到数组首地址
int j = 0;
for (j; j < 5; j++) { //根据首地址找到各个数组元素
printf("%d ", *(parr[i] + j)); //输出
}
putchar('\n');
}
return 0;
}
为了加深理解,我们平时对数组进行的操作也可以对指针数组进行
printf("%d\n", sizeof(parr));
printf("%d\n", sizeof(parr)/sizeof(parr[0]));
这里得到的结果分别是24和3,我用的是x64,指针所占字节数为8。
Part2:
数组指针 指针/数组?类比 首元素vs数组 结合性
数组指针是什么?我们来类比一下,整型指针是指向整型的指针,字符指针是指向字符的指针,顾名思义:数组指针是指向数组的指针。
我们需要先了解一下数组首元素地址和数组地址,之前在初学者指针那块有详细说明,这里回忆一下。
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//arr---首元素地址
//&arr[0]---首元素地址
//&arr---数组地址
指向数组的指针,是这个样子的吗?
int* p = arr;
还是这个样子的?
int* p = &arr[0];
你会发现它们都是指向数组首元素地址的,并不是指向数组地址的(区别在初学者指针那篇有提到),那这样呢?
int* q = &arr;
这样总是指向数组地址了吧?
看起来合情合理,但并不是我们想要的,我们想要的是用一个指针可以指向数组内的所有元素,这里依旧无法用一个指针指向每个元素的地址。那看看下一个怎么样?
int* p[] = { &arr[0],&arr[1],&arr[2]......&arr[8] ,&arr[9] };
//用p[0],p[1]等来指向元素
这样是用一个指针指向所有元素吗?嘿,如果你还记得刚刚看过的指针数组,你可以很明显的发现,这是一个指针数组,也就是说这里的p[0],p[1],并不是一个指针,它们是很多个指针放在一起的,从内存上来看每八个字节放在一块(x64),想象一下八十个字节,每八个就插一块隔板,形成一个个的指针变量,这是指针数组。而数组指针要做的就是把这些隔板拿走,把所有地址都放在一块空间内,形成一个空间为八十字节的指针,也就是数组指针。或许你曾有疑问:指针空间不是放一个地址吗?怎么可能指向所有元素地址?哈哈,这这是数组指针与其他类型指针不同的地方了!那代码要如何写呢?
这里先了解一个结合性[ ]的结合性是高于*号的,如果我们直接写int*p[],这样p和【】结合,是一个数组,什么类型的?int*。那我们可以让*和p先结合,怎么做?加一个()。int(*p)[ ]。*p先表示这是一个指针,之后用int【】表示是整型数组类型的指针,指向整型数组。(这个和设计者有关了,他们想要这样表示也是为了方便写,不会多出一些符号,想象一下有一堆规则和一堆符号,用符号的排列来代表那些规则,经过不断地优化,活下来的符号是为了方便使用和记忆)
int(*p)[10] = &arr; //一个空间为80字节的指针
我们要如何解引用呢?我们知道它是一个指针,一般的指针是如何解引用的?
#include<stdio.h>
int main (void)
{
int arr[]={1,2,3,4,5,6,7,8,9,10};
int* p=arr;
printf("%d",*p); //整型指针的解引用
}
//结果为:1
数组指针要如何做呢?这要先了解数组的【】。
之前说过结合性的问题,这个【】和指针在定义外的地方结合是什么意思?数组吗?
想必大家之前就有了解到数组是一个特别的指针。其【】表示的是对该地址进行运算并解引用,如下:
#include<stdio.h>
int main(void) {
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr; //定义整型指针指向首元素地址
printf("%d\n", arr[1]); //arr表示首元素地址,进行+1运算后解引用
printf("%d\n", p[1]); // p等于首元素地址,进行+1运算后解引用
//二者运算结果均为:2
return 0;
}
我们再来看这一段代码:
int(*p)[10] = &arr;
printf("%d\n",**p); //可以发现p是一个二级指针
//需要解引用两次,但又有些特殊
//运行结果为:1
再来看这一段代码:
printf("%p\n", p);
printf("%p\n", *p);
//程序运行得到的值完全相等,是不是有些诡异了
//这里是一个特殊的地方,我也不清楚为什么这样设计
很明显这里p=&a,一个诡异的二级指针,我们给了它一个名字:指向数组的指针,数组指针,类型:int (*)【】,一种构造类型。正确的代码应该如何去描写,如下:
#include<stdio.h>
int main(void) {
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int(*p)[10] = &arr;
printf("%d", (*p)[0]); //解引用,我们在这里加上一个括号,使它成为一个指向首元素的指针
//然后对其进行运算得到某个位置的地址,再解引用。
return 0;
}
关于&数组名和数组指针的关系可以看这篇: