没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
数组与指针的艺术
1
C 语言---数组与指针的艺术
前言
此文是笔者 2005 年所作《再再论指针》的修订版,与前文相比,本文主要的不同点有
如下几点:
一、引用 C/C++标准的条款去阐述原理。C 与 C++标准共有四个已发行的正式版本,分
别是 C89/C90、C99、C++98 和 C++2003,为了避免重复的条款引用,在文字或语义相同的
情况下,笔者只引用 C99 的条款,遇到不同的情况时再分别引用。
二、加入了 C++的内容。
三、增加或者合并了一些章节,同时修正了一些行文错误。
指针是 C/C++的灵魂!它是 C/C++众多引人入胜的特性中的一朵奇葩。与底层操作的亲
密接触是指针与生俱来的优点,利用指针可以写出许多短小精悍、效率极高的代码。它是
C/C++一把无可替代的利器,凭着这把利器,C/C++与其它高级语言相比至少在效率方面胜
人一筹。
但是,由于数组与指针的原理与使用方式跟人们通常的思维习惯有较大差别,需要花较
多的时间进行消化,这使得对数组与指针的偏见和误解成为了普遍存在的现象,更出现了避
免使用指针的思潮,笔者认为这是非常不可取的。指针是如此犀利,正是它才使得 C/C++语
言威猛无比。如果就这样把它放弃了,那么 C/C++就算是白学了。我们应当让指针成为你手
中那把砍掉索伦手指上魔戒的举世无双的纳西尔圣剑,而不是你心中永远的魔结。
与众多初学者一样,笔者对数组与指针的理解也经历了漫长的过程。初学 C 的时候,笔
者作为谭书的受害者之一,与其它初学者一样脑袋中充满了对数组与指针各种各样的误解。
后来随着对 C/C++理解的深入,逐渐发现谭书中存在大量的谬误与漏洞,从此开始了纠偏的
历程。这是一段“痛,并快乐着”的过程,痛是由于愤恨被谋杀了一段宝贵的时光,快乐是
因为重新找到了方向,相信不少朋友也曾有过跟笔者相似的体会。在这段时间里,笔者查阅
了大量的资料,也耗费了大量的时间进行深刻的思考,还跟同事、朋友、网友进行了大量的
辩论,特别是 2005 年,几乎整整一年的时间都是在激烈的辩论中渡过的,当年这些辩论的
激烈程度到现在还记忆犹新,如果当时手里有把枪的话,我想我会开枪的!经过不断的思考、
辩论、印证,再思考、再辩论、再印证,数组与指针的迷雾终于逐渐清晰了起来。
C/C++
2
本文的目的,是希望通过跟各位朋友一起讨论关于数组与指针的几个关键概念及常见问
题,加深对数组与指针的理解。笔者不敢奢望能够完全解开你心中的魔结,但如果通过阅读
本文,能够让你在日后的数组与指针使用过程中减少失误,笔者就心满意足了。
当你阅读本文后:
如果你有不同的意见,欢迎你在评论里留下自己的见解,笔者很乐意跟你一起讨论,共
同进步。
如果你觉得我说的全都是废话,那么恭喜你,你的指针已经毕业了。
如果你有太多不明白的地方,那么我介绍你先找一些关于数组与指针的读物看看,笔者
推荐你阅读一本叫《C 和指针》的书,看完后再回来继续思考你的问题。
数组与指针概念剖析
数组与指针生来就是双胞胎,多数人就是从数组的学习开始指针的旅程的。在学习的过
程中,很自然就会经常听到或见到关于数组与指针的各种各样的看法,下面我节选一些在各
种论坛和文章里经常见到的文字:
“一维数组是一级指针”
“二维数组是二级指针”
“数组名是一个常量指针”
“数组名是一个指针常量”
……
这些文字看起来非常熟悉吧?类似的文字还有许多。不过非常遗憾,这些文字都是错误
的,实际上数组名永远都不是指针!这个结论也许会让你震惊,但它的确是事实。但是,在
论述这个问题之前,首先需要解决两个问题:什么是指针?什么是数组?这是本章的主要内
容,数组名是否指针这个问题留在第二章进行讨论。看到这里,也许有人心里就会嘀咕了,
这么简单的问题还需要说吗?int *p, a[10];不就是指针和数组吗?但是,笔者在过往的讨论过
程中,还真的发现有不少人对这两个概念远非清晰,这会妨碍对后面内容的理解,所以还是
有必要先讨论一下。
什么是指针?一种普遍存在的理解是,把指针变量理解成就是指针,这种理解是片面的,
指针变量只是指针的其中一种形态,但指针并不仅仅只有指针变量。一个指针,包含了两方
面的涵义:实体(entity)和类型。标准是这样描述指针类型的:
6.2.5 Types
A pointer type may be derived from a function type, an object type, or an incomplete
数组与指针的艺术
3
type, called the referenced type. A pointer type describes an object whose value provides a
reference to an entity of the referenced type. A pointer type derived from the referenced
type T is sometimes called ‘‘pointer to T’’. The construction of a pointer type from a
referenced type is called ‘‘pointer type derivation’’.
请留意第二句所说的内容:指针类型描述了这样一个对象,其值为对某种类型实体的引
用。标准在这里所用的措词是指针类型描述了一个对象。
再来看看标准关于取址运算符&的规定:
6.5.3.2 Address and indirection operators
Semantics
The unary & operator returns the address of its operand. If the operand has type “type”,
the result has type “pointer to type”……. Otherwise, the result is a pointer to the object or
function designated by its operand.
这个条款规定,&运算符的结果是一个指针。但问题是,&表达式的结果不是对象!标准
自相矛盾了吗?当然不是,这说明的是,指针的实体有对象与非对象两种形态。
我们常说的指针变量只是指针实体的对象形态,但对象与非对象两种形态合起来,才是
指针的完整涵义,就是说,无论是否是对象,只要是一个具有指针类型的实体,都可以称之
为指针。换言之,指针不一定是对象,也不一定是变量。后一种情况,指的是当需要产生一
个指针类型的临时对象时,例如函数的传值返回或者表达式计算产生的中间结果,由于是一
个无名临时对象,因此不是变量。
在 C++中,由于引入了 OOP,增加了一种也称为“指针”的实体:类非静态成员指针,虽然
也叫指针,但它却不是一般意义上的指针,说的是 this 指针吧?
C++
类的非静态成员都有
this
指针,包括方法和属性。静态成员不是属于类的某一个对象的,而是属
于整个类的
(
也就是说可以通过类名来访问
)
,因此静态成员不会有
this
指针。
C++标准是这样说的:
3.9.2 Compound types
……. Except for pointers to static members, text referring to “pointers” does not apply
to pointers to members……….
C/C++
4
接下来,该谈谈数组了。数组是一种对象,其对象类型就叫数组类型。但笔者发现有个
现象很奇怪,有些人根本没有数组类型的意识,不过也的确有些书并没有将数组作为一个类
型去阐述,也许原因就在于此吧。数组类型跟指针类型都属于派生类型,标准的条款:
6.2.5 Types
An array type describes a contiguously allocated nonempty set of objects with a
particular member object type, called the element type. Array types are characterized by
their element type and by the number of elements in the array. An array type is said to be
derived from its element type, and if its element type is T, the array type is sometimes
called “array of T”. The construction of an array type from an element type is called “array
type derivation”.
数组类型描述了某种对象的非空集合,不允许 0 个元素,我们有时候看见某个结构定义
内部定义了一个大小为 0 的数组成员,这是柔性数组成员的非标准形式,这个留在第八章讲
述
。数组类型的语法(注意不是数组对象的声明语法)是 element type[interger constant],
例如对于
int a[10];
a 的数组类型描述就是 int[10]。
数组名作为数组对象的标识符,是一个经过“隐式特例化”处理的特殊标识符。整数对
象的标识符、浮点数的标识符等等虽然也是标识符,但数组名与之相比却有重大的区别。计
算机语言存在的目的,是为了将人类的自然语言翻译为计算机能够理解的机器语言,让人类更
加容易地利用和管理各种计算机资源,易用是思想,抽象是方法,语言将计算机资源抽象成
各色各样的语言符号和语言规则,数组、指针、整数、浮点数等等这些东西本质上就是对内
存操作的不同抽象。作为抽象的方法,可以归纳为两种实现,一是名字代表一段有限空间,
其内容称为值;二是名字是一段有限空间的引用,同时规定空间的长度。第一种方法被各种
计算机语言普遍使用,在 C/C++中称为“从左值到右值的转换”。但数组不同于一般的整数、
浮点数对象,它是一个聚集,无法将一个聚集看作一个值,从一个聚集中取值,在 C/C++的
对象模型看来缺乏合理性,是没有意义的。在表达式计算的大多数情况中,第一种方法并不
适合数组,使用第二种方法将数组名转换为某段内存空间的引用更适合。
因此,与一般标识符相比,数组名既有一般性,也有特殊性。一般性表现在其对象性质
数组与指针的艺术
5
与一般标识符是一样的,这种情况下的数组名,代表数组对象,同时由于符合 C/C++的左值
模型,它是一个左值,只不过是不可修改的,不可修改的原因与上一段中叙述的内容相同,
通过一个名字试图修改整个聚集是没有意义的;而特殊性则反映在表达式的计算中,也就是
C/C++标准中所描述的数组与指针转换条款,在这个条款中,数组名不被转换为对象的值,
而是一个符号地址。
现在来看看标准是如何规定数组与指针的转换的:
C89/90 的内容:
6.2.2.1 Lvalues and function designators
Except when it is the operand of the sizeof operator or the unary & operator, or is a
character string literal used to initialize an array of character type. or is a wide string literal
used to initialize an array with element type compatible with wchar-t, an lvalue that has type
“array of type” is converted to an expression that has type “pointer to type” that points to the
initial element of the array object and is not an lvalue.
C99 的内容:
6.3.2.1 Lvalues, arrays, and function designators
Except when it is the operand of the sizeof operator or the unary & operator, or is a
string literal used to initialize an array, an expression that has type “array of type” is
converted to an expression with type “pointer to type” that points to the initial element of the
array object and is not an lvalue. If the array object has register storage class, the behavior
is undefined.
数组类型到指针类型转换的结果,是一个指向数组首元素的类型为 pointer to type 的指
针,并且从一个左值转换成一个右值。经过转换后,数组名不再代表数组对象,而是一个代
表数组首地址的符号地址,并且不是对象。特别指出的是,数组到指针的转换规则只适用于
剩余37页未读,继续阅读
资源评论
路飞大大
- 粉丝: 19
- 资源: 9
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功