数组和指针的关系

时间:2022-03-29 17:06:08

数组和指针总结

初学者往往知道数组和指针之间有联系,但是具体的联系和限制却又搞不清楚,想当年我也如此,这里就将这方面的知识做个总结吧。

1.定义

定义数组时必须指定数组的类型和大小,
定义指针时只需要指定类型。

2.存储空间的分配


对于数组,因为系统会按照你指定的大小为数组分配存储空间,这也是为什么数组必须指定大小的原因,如:char array[5] ; //系统会自动为其预留sizeof(char)*5个字节的连续内存(注意是连续的)。所以我们可以对array[0]...array[4]这五个变量随便访问(读和写)都不会有问题。
对于指针,系统只会为所定义的指针变量分配空间,指针所指向的地点并未分配。举个例子: char *p ; 这里会为变量p分配空间,大小为4字节(32位机),但是*p(就是p指向的地方)却是随机的地方,这个地方系统也不为其分配空间。所以在这种情况下,你访问和给p赋值(p=...)都是允许的,但是访问*p或者给*p赋值都是错误的。我们要想使用*p必须先使其指向有效区域,这可以通过动态申请内存或者赋值(将知道的有效地点赋给它)来实现。
提醒一下:对于指针,在使用时,不光所指向的区域能读写,指针变量本身也能读写,但是数组不同,数组名的不能写的(允许读)。为什么?因为指针变量p有存储空间,而数组名array是没有的。

3.多级指针


一级指针,char *p ; //p为指向char类型的指针变量。

有了指针变量我们就可以定义任何类型的指针,可以随心所欲的用指针来访问任何类型的变量。但是有人发现了问题,使用这种定义,我们无法用指针来操纵指针类型,于是二级指针的定义被拿出来了。

二级指针就是指向指针的指针,用来操纵一指针变量。
例:char **pp ;
定义的pp为指向一个 指向一char变量的指针 的指针。
我们可以将 pp指向到前面定义的p : pp = &p ;
现在很容易看清他们之间个关系:
*pp就是访问pp指向的位置,访问的值当然就是p,
**pp就是*(*pp)拉,当然等于*p.

注意 定义pp的时候,系统只为pp分配了空间,并未为*pp分配空间,后来能访问*pp是因为我们使用pp=&p将pp指向了&p这个已经分配好了的空间。

同理,为了控制二级指针于是有了三级指针,...

4.多维数组

char array[5];//定义了array为含有5个char元素的数组。
二维数组的定义我就不多说了,相信大家知道得已经很详细了。我这里就说一说他们的名称吧。提醒一下:他提出的愿意是我们希望得到array类型的数组。
char arrayMulti[3][5] ;
前面说过数组名不是变量,那么它是怎么使用的呢?
其实数组名只是在编译时刻编译器用来定位变量位置的一个标签。比如 array[3],那么编译器就认为是“标签+3”,你如果写 array[6],那么编译器就认为是“标签+6”,这就是C不会给出数组越界错误的原因。那么arrayMulti是什么呢?细读二维数组的定义:我们定义的是一个数组的数组。我们写了这个定义,就意味着我们已经有了 char [5] 这种类型的数组,而现在我们要定义一个现有数组类型的数组arrayMulti 。并且在以后的程序中他会永远记得它的元素的类型是char [5],所以你在访问他的元素arrayMulti[1]时,他知道这是一个char[5]的数组。同样二维数组也不检查越界,所以你写arrayMulti[2][5]不会有问题。
提示:编译器不检查数组的越界,但会检查数组元素的类型,所以:
对于array ,你给array[0]一个int型的值是不行的,因为他的元素是char类型;
对于arrayMulti,你他的元素一个char[6]的值也是不行的(详细例子见“蜕化”)。

5.数组名的蜕化


数组在作为函数参数时,数组名将蜕化为指针。C语言的书上是这么说的,我这里要说得是:这句话是不完全正确的!我们知道指针是占用内存的,但是这个蜕化而成的家伙是不占有内存的,仍然只是个标签。书上为什么这么说呢?书上的意思是说这家伙已经蜕化得不知道自己有几个元素了。举个例子:
void fun(char array[5]);
在编译时编译器会当成是:void fun(char *array);你在这个函数中使用sizeof(a)得到的值是4,而在定义char array[5]的函数中sizeof(array) = 5,说明确实已经蜕化为指针了。所以你写:
char *pa = array ; //正确,指针到指针

数组名退化为指针,在这里强调一下:数组的元素类型仍然存在!这里要注意的是多维数组的情况。
以2维数组为例:
void fun2(char arrayMulti[3][5]);
那么在函数fun2中,arrayMulti蜕化成的是char (*)[5],即:指向char[5]类型的指针,因为前面分析过arrayMulti的元素的类型是char[5],所以在程序中:
char **pm = arrayMulti ; //错误:从char (*)[5]到 char **的赋值
char (*pm5)[5] ;
pm5 = arrayMulti ; //正确。

6.对元素的访问
数组名是一个标签,是一个记录地址的标签,在对元素的访问上所起的作用和指针一样,所以:
int a[5];
int *b;
b=a;
那么:
a[1],b[1],*(a+1),*(b+1)都是允许的。
更悬一点:因为其在这儿所起的作用仅仅是个标签而已(编译的时候就是让两者相加),所以:
1[a],1[b],也是允许的。

7.取地址
对指针变量使用&运算后得到的是指针变量的地址,那么数组名呢?
他仅仅是个标签啊,根本就没地方存放。
所以C语言中的规定是取数组地址的结果是:仍然是其本身。
既 &array 的值和 array一样。
注意:值是一样,类型是不一样的。

------------------------

虽然数组名的确没有存储空间,但把&a规定为跟a的地址值一样并不是这个原因。
数组名是一个右值,本来不符合&的语法的,但是,数组却是一个对象,对一个
数组对象取地址是合理的,C标准委员会经过衡量,认为维护一个对象的完整性
更重要,因此允许&a,只不过,&a的意义,并非对一个数组名取地址,而是对
一个数组对象取地址。