指针数组和数组指针
1、指针数组
指针数组,可以说成存储指针的数组,这个变量是一个数组,数组里面存储的是指针。例如:
int* a[10];//指针数组,有10个指针的数组,这10个指针指向的是int型数
指针数组,有10个指针的数组,这10个指针指向的是int型数
2、数组指针
数组指针,可以说成指向数组的指针,这个变量是一个指针,而这个指针指向的是一个数组。就是说这个指针里存放的是一个数组的首地址,或者指向一个数组的首地址。例如:
int(*a)[10];//数组指针,指针a指向一个大小为10的数组,数组中存放的是int型数
数组指针,指针a指向一个大小为10的数组,数组中存放的是int型数
3、下面据一些小的例子更加清楚地说明:*
#include <stdio.h>
int main() {
char* a[4] = { "hello","world","zhao","zuishuai" };//指针数组,有4个指针的数组,每个指针指向一个char
printf("%s\n",a[0]);
//相当于char *p1=hello,*p2=world,*p3=zhao,*p4=zuishuai
// 指针赋值:char*p1这是创建了一个char*类型的p1 赋值应该是地址(p1是个指针,存的是一个地址,地址指向一个char)
int b[4] = { 0 };
int(* p)[4] = NULL;//数组指针,其本质是指向一个有4个int型数组的指针
p = &b;//把数组的地址给到数组指针(注意是数组的指针,不是首元素指针;区别于b)
printf("%p\n", p);
printf("%p\n", b);
printf("%p\n",p+1);//数组的地址 p+1是+一个b[4],所以可以定义成二维数组p是一行
printf("%p\n",b+1);//数组首地址 b+1是+一个int
}
输出的结果:
在看代码之前我们先要了解一个内存映像图:
-
上面代码首先定义了一个指针数组a,有的人该问了,指针数组里面不应该从存放的是指针吗,怎么直接存数据了?(char* a[4] = {
“hello”,“world”,“zhao”,“zuishuai” };)
其实是这样的,a存放在栈区,是一个有四个元素的数组,而数组的每一个数都是一个指针,所以各元素占的字节大小为4,a数组里面确实存放的是指针,只不过这几个指针是直接指向的char类型常量了。也就是a[4]数组存放在栈区,而数组a[4]的4个指针元素分别指向数据区的4个字符的首地址。相当于是定义了char
*p1=“hello” ,char *p2=“world” ,char *p3=“zhao” ,char *p4=“zuishuai”;所以当输出a[0]的时候相当于是输出 *p1 。 -
接下来定义了一个int类型的数组,并附初值全为0;之后是一个数组指针,因为他是一个指向大小为4的int类数组的指针,所以应该赋值是一个地址,此处赋值为NULL;
接下来特别要注意数组指针与数组名的区别,数组指针是指向整个数组地址(数组首地址),而数组名是数组首元素的地址。所以若用p=b;赋值是不对的,应该用 p=&b; 因为从代码中可以看出,不管是数组指针还是数组名,起始地址都是一样的,但是当进行+1操作的时候,数组名只是移动了4B(一个元素int的大小),而数组指针移动了整个数组16B的大小,所以两者并不等价。
4、指针数组和数组指针的使用
要明白指针数组和数组指针的使用,及需要明确两个点:二级指针(指针的指针) 和 指向指针数组的指针
- 二级指针(指针的指针)
指针的指针需要用到两个星号,三个星号就叫指针的指针的指针。例如:
int c=‘A’; int *p=&c; int **cp=&p;
通过指针的指针,不仅可以访问他只指向的指针,还可以访问它指向的指针所指向的数据。
#include <stdio.h>
int main() {
int c = 123;
int* p = &c;
int** cp = &p;
printf("%d\n",c);//A
printf("%d\n", *p);//A
printf("%d\n", p);//&
printf("%d\n", cp);
printf("%d\n", *cp);
printf("%d\n", **cp);//
}
运行结果:
通过上面的例子可以加深理解:
首先是定义了一个int类型变量c,之后定义了一个指向c的指针p,指针p里面存放的是c的地址。之后定义了一个指向p指针的指针cp,cp里面存放的是p指针的地址,但是可以通过*cp指针寻找到指针p的地址,用**cp可以找到p指针指向的地址。如图:
利用指针的指针可以允许被调用函数修改局部指针变量和处理指针数组:
void FindCredit(int **);
main(){
int vals[]={7,6,5,-4,3,2,1,0};
int *fp=vals;
FindCredit(&fp);
printf("%d\n",*fp);
}
void FindCredit(int **fpp) {
while(**fpp!=0)
if(**fpp<0) break;
else (*fpp)++;
}
首先用一个数组的地址初始化指针fp,然后把该指针的地址作为实参传递给函数FindCredit()。FindCredit()函数通过表达式*fpp间接地得到数组中的数据。为遍历数组以找到一个负值,FindCredit()函数进行自增运算的对象是调用者的指向数组的指针,而不是它自己的指向调用者指针的指针。语句(*fpp)++就是对形参指针指向的指针进行自增运算的。但是因为运算符高于++运算符,所以圆括号在这里是必须的,如果没有圆括号,那么++运算符将作用于二重指针fpp上。
- 指向指针数组的指针
char *Names[]= {
"Bill", "Sam",
"Jim", "Paul",
"Charles", 0
};
main(){
char **nm=Names;
while(*nm!=0)
printf("%s\n",*nm++);
}
指针数组里面的元素本身就是指针,所以用指针的指针来调用这些元素。首先将数组Names数组的地址给nm指针,在while中判断*nm(也就是数组Names里面的元素地址(指针地址))是不是0,不是0进入循环,每次printf()的调用都首先传递指针nm指向的字符型指针,然后对nm进行自增运算使其指向数组的下一个元素(还是指针)。
tips:
如果是向子函数传参,这和传递一个普通数组的思想一样,不能传递整个数组过去,如果数组很大,这样内存利用率很低,所以应该传递数组的首地址,用一个指针接收这个地址。因此,指针数组对应着二级指针。(传递的时指针数组的首地址)
本文参考:这篇文章