没有合适的资源?快使用搜索试试~ 我知道了~
再再论指针 pdf格式 电子书 下载
需积分: 3 12 下载量 27 浏览量
2008-11-28
16:18:46
上传
评论
收藏 343KB PDF 举报
温馨提示
试读
20页
对指针的精彩讲解。令C语言学习者非常收益。对领悟C语言的内部奥妙很有帮助。个人收藏,欢迎大家下载。
资源详情
资源评论
资源推荐
再再论指针----篇首语
指针是 C 语言规范里面一项核心内容,指针具有与生俱来的优势,利用指针可以写出许
多短小精悍、效率极高的代码,它是 C 语言一把无可替代的利器,凭着这把利器,C 语言与其
它高级语言相比至少在效率方面高人一筹。但是,由于指针的原理与使用方式跟人们通常的思维
习惯有较大的差别,造成了指针比 C 语言其它概念难理解得多,这使得对指针认识不足成为了
一种在 C 程序员中普遍存在的现象,这种不足必然导致程序员在指针的使用过程中不断遭受挫
折,挫折多了,指针俨然变成一道无法逾越的难关,恐惧感也就油然而生了。在恐惧感面前,某
些程序员甚至产生了要避免使用指针的念头,这是非常不可取的。指针是如此犀利,正是它才使
得 C 语言威猛无比,如果就这样把它放弃了,那么 C 语言就算是白学了。我们应当让指针成为
你手中那把砍掉索伦手指上魔戒的举世无双的纳西尔圣剑,而不是成为你心中永远的魔戒。
本文的目的,是希望通过跟各位朋友一起讨论关于指针的几个关键概念及常见问题,以加
深对指针的理解。因此,本文并不是讲述形如 int *p、struct {int i;float j;} *p 等这些东西
是什么的文章,阅读本文的朋友最好对指针已经具有一定的使用经验,正因如此,笔者才给文章
起名叫《再再论指针》。笔者不敢奢望能够完全解开你心中的魔结,但如果通过阅读本文,能够
让你在日后的指针使用过程中减少失误,那笔者就心满意足了。本文将讨论如下十个主题,读者
最好按主题的顺序一个一个地阅读,当然,如果你只对其中某个或某几个主题感兴趣,只看那几
个也未尝不可。
当你阅读本文后:
如果你有不同的意见,欢迎你在评论里留下自己的见解,笔者很乐意跟你一起讨论,共同
进步。
如果你觉得我说的全都是废话,那么恭喜你,你的指针已经毕业了。
如果你有太多不明白的地方,那么我介绍你先找一些关于数组与指针的读物看看,笔者推
荐你阅读一本叫《C 与指针》的书,看完后再回来继续思考你的问题。
1、什么是数组名?----一个让你吃惊的事实!
2、再一次吃惊!----数组的数组与多维数组的区别
3、数组的解剖学
4、[ ]运算符的本质
5、指向数组的指针
6、“另类”数组
7、C语言声明详解
8、右左法则----复杂指针解析
9、指针与const
10、围绕p()与(*p)()的争论
11、《再再论指针》后记 2005.10.17 日补充
第一章 什么是数组名?----一个让你吃惊的事实!
数组是指针的基础,多数人就是从数组的学习开始指针的旅程的。下面我节选一些在各种
论坛和文章里经常见到的关于数组的文字:
“一维数组是一级指针”
“二维数组是二级指针”
“数组名可以作为指针使用”
“数组名就是..........的常量指针”
“数组名就是..........的指针常量”
..................................
这些文字看起来非常熟悉吧?类似的文字还有许多,或许你就是经常说这些话的人呢。不过非常
遗憾,这些文字都是错误的,实际上数组名永远都不会是指针!这个结论也许会让你震惊,但它
的确是事实。数组名、指针、地址这几个概念虽然是基础中的基础,但它们恰恰是被混淆和滥用
得最多的概念,把数组名说成指针,是一个概念性的错误,实质是混淆了指针与地址两个概念的
本质。俗话说得好:浅水淹死人。因此,在讨论数组之前,有必要先回过头来澄清一下什么是指
针,什么是地址,什么是数组名。
指针是 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 了。当形参是指针的时候,所发生的事情跟这个是相同的。指针形参并没有要求实参也是一
个指针,它需要的是一个地址,谁能给予它一个地址?显然指针、地址常量和符号地址常量都能
满足这个要求,而数组名作为符号地址常量正是指针形参所需要的地址,这个过程就跟把一个整
数赋值给一个整数变量一样简单!
在后面的章节中,笔者将严格地使用地址这一概念,该是地址时就用地址,该是指针时就
用指针,以免象其它教材那样给读者一个错误的暗示。
第二章 再一次吃惊----数组的数组与多维数组的区别
看见这个题目,也许有些人就会嘀咕了:难道两者不是一样的吗?C 语言的多维数组不就
是数组的数组吗?不!两者是有区别的,而且还不小呢。首先看看两者的共同点:
1。内存映象一样。
2。数组引用方式一样,都是“数组名[下标][下标]........”。
3。数组名都是数组的首地址,都是一个符号地址常量、一个右值。
由于两者的共同点主要反映在外部表现形式上,因此,从外部看来,数组的数组跟多维数
组似乎是一样的,这造成了 C 程序员对两者的区别长期以来模糊不清。但实际上,c 语言限于本
身的语言特性,实现的并非真正的多维数组,而是数组的数组。
数组的数组与多维数组的主要区别,就在于数组的数组各维之间的内在关系是一种鲜明的
层级关系。上一维把下一维看作下一级数组,也就是数组嵌套。数组引用时需要层层解析,直到
最后一维。举个例,对于数组:
int a[7][8][9];
如果要访问元素 a[4][5][6],首先就要计算第一维元素 4 的地址,也就是 a+4,由于是数组的
数组,元素 4 的值代表了一个数组,因此元素 4 的值就是它所代表的那个数组的首地址,我们
用一个符号 address1 代表它,也就是 address1=*(a+4),接着计算第二维,显然元素 5 的
地址是 address1+5,其值也是一个数组的首地址,用 address2 表示它,就是
address2=*(address1+5),最后一维,由于已经到达了具体的元素,因此这个元素的地址是
address2+6,其值*(address2+6)是一个整数,把 address1 和 address2 分别代入相应表
达式,就成了:
*(*(*(a+4)+5)+6);
这就是我们熟知的[]运算符的等价表达式。
而真正的多维数组并没有这么多“束缚”,相比之下简单得多,由于各维之间不是这种复杂
的层级关系,元素 a[4][5][6]的偏移量可以这样直接获得:(4x8x9+5x9+6)xsizeof(int),
再加上数组的首地址 a 就是元素 a[4][5][6]的地址了。但是,c 语言的数组能够这样用首地址
加上(4x8x9+5x9+6)xsizeof(int)的形式来访问元素吗?显然是不行的。归根到底就在于 C
语言的地址数据类型不但有类型,还具有级别。就是这种层级关系造成了 C 语言只能用数组的
数组当作多维数组。如果 C 语言非得要实现真正的多维数组,那么地址与指针的概念就得重新
改写了。
第三章 数组的解剖学
这一章我们来讨论一下数组的内涵,对数组的内部构造进行一次解剖,看看里面究竟隐藏
了什么秘密。 有了前面两章对数组名和 C 语言数组本质的澄清,再来理解这一章的内容,就容
易多了。
剩余19页未读,继续阅读
lsk_30514
- 粉丝: 0
- 资源: 1
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
评论0