数组和指针的本质区别

来源:互联网 发布:win10网络图标不见了 编辑:程序博客网 时间:2024/05/19 18:46

 

 

 

数组是一种数据结构,指针是一种真正的指针

编译器知道的是数组的第一个元素的地址,而编译器只知道的是指针变量所在的地址


char a[10];

char *p;

char ch;

ch = a[1]ch = *p在操作上的差别:

编译器要找到数组的内容只需要两步:1、获取数组第一个元素的地址,并加上偏移量,形成一个地址;2、从该地址获取数值;

       而指针的操作是:1、获取指针变量所在的地址;2、获取指针变量的值;3、以指针变量的值为地址获取所要的值。

       要知道两种表示的本质差别应当从编译器的层次来考量,或者从生成的汇编代码来考量才可以认识其本质,不然也只是雾里看花。

 

    指针是C语言具有低级语言特征的最直接的证据。在汇编语言里面,指针的概念随
处可见。比如SP,SP寄存器又叫堆栈指针,它的值是地址,由于SP保存的是地址,并且SP
的值是不断变化的,因此可以看作一个变量,而且是一个地址变量。地址也是C语言指针的
值,C语言的指针跟SP这样的寄存器虽然不完全一样,但原理却是相通的。C语言的指针也
是一种地址变量,C89明确规定,指针是一个保存对象地址的变量。这里要注意的是,指针
跟地址概念的不同,指针是一种地址变量,通常也叫指针变量,统称指针。而地址则是地
址变量的值。

看到这里,也许你会觉得,这么简单的东西还用你来说吗?的确,对于p与&p来说
,99%的人都能在0.1秒内脱口而出谁是指针,谁是地址,但是,又有多少人在使用指针的
过程中能够始终如一毫不动摇地遵循这两个概念呢?不少人使用指针的时候就会自觉或不
自觉地把指针和地址两个概念混淆得一塌糊涂了,数组名的滥用就是一个活生生的例子。
这一点甚至连一些经典著作也没能避免。

不过也不能全怪你自己,笔者认为某些国内教材应该承担最大的责任。这些教材
一开始就没有给读者好好地分清指针与地址的区别,相反还在讲述的过程中有意无意地混
用这两个概念。更有甚者,甚至在书中明言指针就是地址!说这话的家伙最应该在C语言这
个地图上抹掉,呵呵。两个月前我在购书中心随手翻开了某个作者主编的一本被冠以国家
“十五”规划重点研究项目的书,书里就是这么写的。当时笔者就感慨:不知道又要有多
少人的思想被这家伙“强奸”了。

实际上,地址这个东西,本来就是一种基本数据类型,本应该在介绍整数、浮点
、字符等基本类型的时候把地址显式地放在一起讨论,这样在后面介绍指针与数组的时候
就能避免许多误解。可惜不少教材或者根本没有谈及,或者就算提起这个类型也用了指针
类型这个字眼。这就错了,指针不是类型,真正的类型是地址,指针只是存储地址这种数
据类型的变量!打个比方,对于
int i=10;
10是整数,而i是存储整数的变量,指针就好比这个i,地址就好比那个10。指针能够进行
加减法,原因并不是因为它是指针,加减法则不是属于指针这种变量的,而是地址这种数
据类型的本能,正是因为地址具有加减的能力,所以才使指针作为存放地址的变量能够进
行加减运算。这跟整数变量因为整数能够进行加减乘除因而它也能进行加减乘除一个道理



那么数组名又应该如何理解呢?用来存放数组的区域是一块在栈中静态分配的内
存(非static),而数组名是这块内存的代表,它被定义为这块内存的首地址。这就说明了
数组名是一个地址,而且,还是一个不可修改的常量,完整地说,就是一个地址常量。数
组名跟枚举常量类似,都属于符号常量。数组名这个符号,就代表了那块内存的首地址。
注意了!不是数组名这个符号的值是那块内存的首地址,而是数组名这个符号本身就代表
了首地址这个地址值,它就是这个地址,这就是数组名属于符号常量的意义所在。由于数
组名是一种符号常量,因此它是一个右值,而指针,作为变量,却是一个左值,一个右值
永远都不会是左值,那么,数组名永远都不会是指针!不管什么话,只要说数组名是一个
指针的,都是错误的!就象把刚才int i=10例子中的10说成是整数变量一样,在最基本的
立足点上就已经完错了。

总之要牢牢记住,数组名是一个地址,一个符号地址常量,不是一个变量,更不
是一个作为变量的指针!

在数组名并非指针这个问题上,通常会产生两种疑问:
1。作为形参的数组,不是会被转换为指针吗?
2。如果形参是一个指针,数组名可以作为实参传递给那个指针,难道不是说明了数组名是
一个指针吗?

首先,C语言之所以把作为形参的数组看作指针,并非因为数组名可以转换为指针
,而是因为当初ANSI委员会制定标准的时候,从C程序的执行效率出发,不主张参数传递时
复制整个数组,而是传递数组的首地址,由被调函数根据这个首地址处理数组中的内容。
那么谁能承担这种“转换”呢?这个主体必须具有地址数据类型,同时应该是一个变量,
满足这两个条件的,非指针莫属了。要注意的是,这种“转换”只是一种逻辑看法上的转
换,实际当中并没有发生这个过程,没有任何数组实体被转换为指针实体。另一方面,大
家不要被“转换”这个字眼给蒙蔽了,转换并不意味着相同,实际上,正是因为不相同才
会有转换,相同的话还转来干吗?这好比现在社会上有不少人“变性”,一个男人可以“
转换”为一个女人,那是不是应该认为男人跟女人是相同的?这不是笑话么。

第二,函数参数传递的过程,本质上是一种赋值过程。C89对函数调用是这样规定
的:函数调用由一个后缀表达式(称为函数标志符,function designator)后跟由圆括号
括起来的赋值表达式列表组成,在调用函数之前,函数的每个实际参数将被复制,所有的
实际参数严格地按值传递。因此,形参实际上所期望得到的东西,并不是实参本身,而是
实参的值或者实参所代表的值!举个例来说,对于一个函数声明:

void fun(int i);

我们可以用一个整数变量int n作实参来调用fun,就是fun(n);当然,也正如大家所熟悉
的那样,可以用一个整数常量例如10来做实参,就是fun(10);那么,按照第二个疑问的看
法,由于形参是一个整数变量,而10可以作为实参传递给i,岂不就说明10是一个整数变量
吗?这显然是谬误。实际上,对于形参i来说,用来声明i的类型说明符int,所起的作用是
用来说明需要传递给i一个整数,并非要求实参也是一个整数变量,i真正所期望的,只是
一个整数,仅此而已,至于实参是什么,跟i没有任何关系,它才不管呢,只要能正确给i
传递一个整数就OK了。当形参是指针的时候,所发生的事情跟这个是相同的。指针形参并
没有要求实参也是一个指针,它需要的是一个地址,谁能给予它一个地址?显然指针、地
址常量和符号地址常量都能满足这个要求,而数组名作为符号地址常量正是指针形参所需
要的地址,这个过程就跟把一个整数赋值给一个整数变量一样简单!

在后面的章节中,笔者将严格地使用地址这一概念,该是地址时就用地址,该是
指针时就用指针,以免象其它教材那样给读者一个错误的暗示。
原创粉丝点击