数组指针和指针数组

首先,理解一下数组指针和指针数组这两个名词:

“数组指针”和“指针数组”,只要在名词中间加上“的”字,就知道中心了——

数组的指针:是一个指针,什么样的指针呢?指向数组的指针。

指针的数组:是一个数组,什么样的数组呢?装着指针的数组。

然后,需要明确一个优先级顺序:()>[]>*,所以:

(*p)[n]:根据优先级,先看括号内,则p是一个指针,这个指针指向一个一维数组,数组长度为n,这是“数组的指针”,即数组指针;

*p[n]:根据优先级,先看[],则p是一个数组,再结合*,这个数组的元素是指针类型,共n个元素,这是“指针的数组”,即指针数组。

根据上面两个分析,可以看出,p是什么,则词组的中心词就是什么,即数组“指针”和指针“数组”。
 

int *p1[5];
int (*p2)[5];

首先,对于语句“int*p1[5]”,因为“[]”的优先级要比“*”要高,所以 p1 先与“[]”结合,构成一个数组的定义,数组名为 p1,而“int*”修饰的是数组的内容,即数组的每个元素。也就是说,该数组包含 5 个指向 int 类型数据的指针,如图 1 所示,因此,它是一个指针数组。

                                       图1 

其次,对于语句“int(*p2)[5]”,“()”的优先级比“[]”高,“*”号和 p2 构成一个指针的定义,指针变量名为 p2,而 int 修饰的是数组的内容,即数组的每个元素。也就是说,p2 是一个指针,它指向一个包含 5 个 int 类型数据的数组,如图 2 所示。很显然,它是一个数组指针,数组在这里并没有名字,是个匿名数组。

                                    

                                                               图2 

由此可见,对指针数组来说,首先它是一个数组,数组的元素都是指针,也就是说该数组存储的是指针,数组占多少个字节由数组本身决定;而对数组指针来说,首先它是一个指针,它指向一个数组,也就是说它是指向数组的指针,在 32 位系统下永远占 4 字节,至于它指向的数组占多少字节,这个不能够确定,要看具体情况。
 

数组指针 (*p)[n]

数组指针:是指针——指向数组的指针。

看下面的例子进行理解

#include "stdafx.h"
 
 
int main()
{
	//一维数组
	int a[5] = { 1, 2, 3, 4, 5 };
	//步长为5的数组指针,即数组里有5个元素
	int (*p)[5];
	//把数组a的地址赋给p,则p为数组a的地址,则*p表示数组a本身
	p = &a;
 
	//%p输出地址, %d输出十进制
	//\n回车
	//在C中,在几乎所有使用数组的表达式中,数组名的值是个指针常量,也就是数组第一个元素的地址,它的类型取决于数组元素的类型。
	printf("%p\n", a); //输出数组名,一般用数组的首元素地址来标识一个数组,则输出数组首元素地址
	printf("%p\n", p); //根据上面,p为数组a的地址,输出数组a的地址
	printf("%p\n", *p); //*p表示数组a本身,一般用数组的首元素地址来标识一个数组
	printf("%p\n", &a[0]); //a[0]的地址
	printf("%p\n", &a[1]); //a[1]的地址
	printf("%p\n", p[0]); //数组首元素的地址
	printf("%d\n", **p); //*p为数组a本身,即为数组a首元素地址,则*(*p)为值,当*p为数组首元素地址时,**p表示首元素的值1
	printf("%d\n", *p[0]); //根据优先级,p[0] 表示首元素地址,则*p[0]表示首元素本身,即首元素的值1
	printf("%d\n", *p[1]); //为一个绝对值很大的负数,不表示a[1]...表示什么我还不知道
 
	
 
	//将二维数组赋给指针
	int b[3][4];
	int(*pp)[4]; //定义一个数组指针,指向含4个元素的一维数组
	pp = b; //将该二维数组的首地址赋给pp,也就是b[0]或&b[0],二维数组中pp=b和pp=&b[0]是等价的
	pp++; //pp=pp+1,该语句执行过后pp的指向从行b[0][]变为了行b[1][],pp=&b[1]
 
	int k;
	scanf_s("%d", &k);
 
    return 0;
}


 

根据上面二维数组可以得出,数组指针也称指向一维数组的指针,所以数组指针也称行指针。

指针数组 *p[n]

指针数组:是数组——装着指针的数组。

看下面的例子进行理解:

#include "stdafx.h"
 
 
int main()
{
	int a = 1;
	int b = 2;
	int *p[2];
	p[0] = &a;
	p[1] = &b;
 
	printf("%p\n", p[0]); //a的地址
	printf("%p\n", &a); //a的地址
	printf("%p\n", p[1]); //b的地址
	printf("%p\n", &b); //b的地址
	printf("%d\n", *p[0]); //p[0]表示a的地址,则*p[0]表示a的值
	printf("%d\n", *p[1]); //p[1]表示b的地址,则*p[1]表示b的值
 
 
	//将二维数组赋给指针数组
	int *pp[3]; //一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2],所以要分别赋值
	int c[3][4];
	for (int i = 0; i<3; i++)
		pp[i] = c[i];
 
	int k;
	scanf_s("%d", &k);
 
    return 0;
}
 

最后,从上文来看:

数组指针是一个指针变量,占有内存中一个指针的存储空间;

指针数组是多个指针变量,以数组的形式存储在内存中,占有多个指针的存储空间。

 

了解指针数组和数组指针二者之间的区别之后,继续来看下面的示例代码:

int arr[5]={1,2,3,4,5};
int (*p1)[5] = &arr;
/*下面是错误的*/
int (*p2)[5] = arr;

不难看出,在上面的示例代码中,&arr 是指整个数组的首地址,而 arr 是指数组首元素的首地址,虽然所表示的意义不同,但二者之间的值却是相同的。那么问题出来了,既然值是相同的,为什么语句“int(*p1)[5]=&arr”是正确的,而语句“int(*p2)[5]=arr”却在有些编译器下运行时会提示错误信息呢(如在 Microsoft Visual Studio 2010 中提示的错误信息为“a value of type"int*"cannot be used to initialize an entity of type"int(*)[5]"”)?

其实原因很简单,在 C 语言中,赋值符号“=”号两边的数据类型必须是相同的,如果不同,则需要显示或隐式类型转换。在这里,p1 和 p2 都是数组指针,指向的是整个数组。p1 这个定义的“=”号两边的数据类型完全一致,而 p2 这个定义的“=”号两边的数据类型就不一致了(左边的类型是指向整个数组的指针,而右边的数据类型是指向单个字符的指针),因此会提示错误信息。

  • 1440
    点赞
  • 5557
    收藏
    觉得还不错? 一键收藏
  • 91
    评论
指针数组数组指针的区别 数组指针(也称行指针) 定义 int (*p)[n]; ()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。 如要将二维数组赋给一指针,应这样赋值: int a[3][4]; int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。 p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0] p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][] 所以数组指针也称指向一维数组指针,亦称行指针指针数组 定义 int *p[n]; []优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1是错误的,这样赋值也是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。 如要将二维数组赋给一指针数组: int *p[3]; int a[3][4]; for(i=0;i<3;i++) p[i]=a[i]; 这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2] 所以要分别赋值。 这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。 还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。 比如要表示数组中i行j列一个元素: *(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j] 就指向指针指针,很早以前在说指针的时候说过,但后来发现很多人还是比较难以理解,这一次我们再次仔细说一说指向指针指针。  先看下面的代码,注意看代码中的注解: #include <iostream>  #include <string>  using namespace std;    void print_char(char* array[],int len);//函数原形声明    void main(void)    {  //-----------------------------段1-----------------------------------------      char *a[]={"abc","cde","fgh"};//字符指针数组      char* *b=a;//定义一个指向指针指针,并赋予指针数组首地址所指向的第一个字符串的地址也就是abc\0字符串的首地址      cout<<*b<<"|"<<*(b+1)<<"|"<<*(b+2)<<endl;  //-------------------------------------------------------------------------    //-----------------------------段2-----------------------------------------      char* test[]={"abc","cde","fgh"};//注意这里是引号,表示是字符串,以后的地址每加1就是加4位(在32位系统上)      int num=sizeof(test)/sizeof(char*);//计算字符串个数      print_char(test,num);      cin.get();  //-------------------------------------------------------------------------  }    void print_char(char* array[],int len)//当调用的时候传递进来的不是数组,而是字符指针他每加1也就是加上sizeof(char*)的长度  {      for(int i=0;i<len;i++)      {          cout<<*array++<<endl;      }  }   下面我们来仔细说明一下字符指针数组和指向指针指针,段1中的程序是下面的样子: char *a[]={"abc","cde","fgh"};  char* *b=a;  cout<<*b<<"|"<<*(b+1)<<"|"<<*(b+2)<<endl;   char *a[]定义了一个指针数组,注意不是char[], char[]是不能同时初始化为三个字符的,定义以后的a[]其实内部有三个内存位置,分别存储了abc\0,cde\0,fgh\0,三个字符串的起始地址,而这三个位置的内存地址却不是这三个字符串的起始地址,在这个例子中a[]是存储在栈空间内的,而三个字符串却是存储在静态内存空间内的const区域中的,接下去我们看到了char* *b=a;这里是定义了一个指向指针指针,如果你写成char *b=a;那么是错误的,因为编译器会返回一个无法将char* *[3]转换给char *的错误,b=a的赋值,实际上是把a的首地址赋给了b,由于b是一个指向指针指针,程序的输出cout<<*b<<"|"<<*(b+1)<<"|"<<*(b+2)<<endl;   结果是 abc cde fgh   可以看出每一次内存地址的+1操作事实上是一次加sizeof(char*)的操作,我们在32位的系统中sizeof(char*)的长度是4,所以每加1也就是+4,实际上是*a[]内部三个位置的+1,所以*(b+1)的结果自然就是cde了,我们这时候可能会问,为什么输出是cde而不是c一个呢?答案是这样的,在c++中,输出字符指针就是输出字符串,程序会自动在遇到\0后停止.   我们最后分析一下段2中的代码,段2中我们调用了print_array()这个函数,这个函数中形式参数是char *array[]和代码中的char *test[]一样,同为字符指针,当你把参数传递过来的时候,事实上不是把数组内容传递过来,test的首地址传递了进来,由于array是指针,所以在内存中它在栈区,具有变量一样的性质,可以为左值,所以我们输出写成了,cout<<*array++<<endl;当然我们也可以改写为cout<<array[i]<<endl,这里在循环中的每次加1操作和段1代码总的道理是一样的,注意看下面的图!   到这里这两个非常重要的知识点我们都说完了,说归说,要想透彻理解希望读者多动手,多观察,熟能生巧。   下面是内存结构示意图:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 91
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值