《c数组与指针》word版.doc_第1页
《c数组与指针》word版.doc_第2页
《c数组与指针》word版.doc_第3页
《c数组与指针》word版.doc_第4页
《c数组与指针》word版.doc_第5页
已阅读5页,还剩6页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

c 数组与指针 2c数组与指针(2)2010-04-05 11:40第四章运算符的本质数组是存在于人们头脑中的一个逻辑概念,而编译器其实并不知道有数组这个东西,它所知道的,只是运算符,当遇到运算符的时候,编译器只是简单地把它转换为类似*(*(a+i)+j)这样的等价表达式,之所以是这种表达式,如前几章所述,是因为C语言的数组实现本质上是数组的嵌套。由于这种等价关系的存在,会产生一些古零精怪的表达式,例如:10a这个表达式初看上去让人摸不着头脑,它是什么呢?如上所述,编译器会把它转换为*(10+a),把a和10调换一下,就是*(a+10)了,这个就是a10。运算符之前还可以是一个表达式,例如:(10+20)a。严格来讲,以上两个表达式是非法的,因为C89对于数组的引用(注意不是数组定义)规定:带下标的数组引用后缀表达式由一个后缀表达式后跟一个括在方括号中的表达式组成。方括号前的后缀表达式的类型必须为指向T类型的指针,其中T为某种类型;方括号中表达式的类型必须为整型。这个规定说明,进行数组引用的时候,运算符的左边并非必须为数组名,而可以是一个表达式,但这个表达式的类型必须为指向某类型的指针。显然10跟(10+20)连地址都不是,因此实际上他们是非法的,编译器在这里并没有严格遵守标准的规定。但如果是:int a10,*p=a;(p+1)2这样就是合法的,因为p+1的结果仍然是一个指针。要注意的是,虽然后缀表达式是一个指向某类型的指针,但不要被这里所说的指针一词搞混了,上面的规定不能反过来使用。还是以上面的例子为例,我们可以pi这样使用p,这是符合上述规定的,但并不能因为指针p能够以pi这种形式使用就认为p是一个数组,这就错误了,不能反过来应用上述规则。最后说一下编译器对&*的优化,对于数组int a10,如果对其中一个元素取地址,例如&a1,这条表达式等价于&*(a+1),编译器并不会先计算*再运算&,而是对&*两个运算符进行优化,把它们同时去掉,因为两者的作用是相反的,最后得到计算的是a+1表达式。第五章指向数组的指针讲到第五章了,数组两个字还离不开我们的左右,数组的内容也真多,另一方面也因为数组与指针的关系的确非常密切。通常,对于int a89这个二维数组,我们可以这样定义一个指向它的指针:int(*p)9;这个声明的形式跟人们所熟悉的int*p的形式大相庭径,初学者通常会感到迷惑,不理解的地方大致有四个:1。为什么会以这种形式声明?2。(*p)应该如何理解?3。为什么必须把第二维显式地声明?4。为什么忽略第一维?下面我们就一起逐个讨论这四个问题:1。这种形式是C标准的声明语法规定的,由于本章不是对标准的解释,只是对标准的应用,因此笔者尽量以简洁的方式解释这个声明,详细的讨论将在第七章进行。C标准的声明包含了两部分:声明:声明说明符初始化声明符表opt(opt的意思是可选)在声明说明符里面有一项类型说明符,int就是这种类型说明符。而初始化声明符表里面的其中一种形式,就是:直接声明符常量表达式opt(*p)9就是这种直接声明符加的形式。2。p左边的*在这里不是取值运算符,而是一个声明符,它指出p是一个指针。而()括号是不能去掉的,如果去掉了,由于运算符优先级比*高,p就会先跟结合,这样p就变成了一个指针数组,而不是指向数组的指针。题外话:*p还有一种用法,就是当*是取值运算符的时候,*p是一个左值,表示一个变量,为什么*p是一个变量呢?也许有人会说,因为int i,*p=&i嘛,其实这是结果不是原因。严格来说,i只是一个变量名,不是变量,在编译器的符号表里面,变量名是一个符号地址,它所代表的地址值是它指向的那段内存单元的地址,真正叫变量的是那段内存单元,懂汇编的朋友能很容易地区分出来,在汇编里面,可以这样定义一个变量名:VARW DW 10,20 VARW就是一个变量名,它在汇编里面是一个地址,代表了10所在的内存单元这个变量。由于p被初始化为&i,*p指向i所代表的那段内存单元,因此说*p是一个变量。把i称为变量是一种习惯上的统称。3。定义一个指针的时候,首先必须定出指针的类型,由于这是一个指向数组的指针,如果数组的元素的类型定下来了,那么这个指针的类型也就定下来了。前面说过,C语言的多维数组实质上是数组的嵌套,那么所指向数组的元素必定具有数组类型,也就是说,这个数组的元素是一个具有6个int元素的数组,因此,p定义的时候,必须指定第二维的上界,这样才能把p的类型定下来。4。有这种疑问的人已经犯了一个错误,没有分清楚什么是指针,什么是数组,以数组的思维模式来看待这个指针p。定义一个数组(非static)的时候,需要在栈中静态分配一块内存,那么就需要知道这块内存的大小,因此定义数组时需要确定各维的上界。而这里只是定义一个指针而已,对于一个指针的定义,需要知道的是它所指向对象的类型,并不需要知道对象的大小,这是多余的。因此,所有指向数组的指针的第一维被忽略。以上介绍了如何声明一个指向二维数组的指针,类似地,对一个指向n维数组的指针也可以用同样的方法来声明,如下:int(*p)x2x3.xn;同样可以忽略第一维,而其它维必须指定上界。最后再讨论一种很常见的对多维数组的错误理解,有些人常常会以为,二维数组就是二级指针,这种错误的根源,来自于可以把一个二级指针int*p以pij这种形式使用。首先把数组称为指针就是错误的,第一章笔者已经说明了数组名是地址,不能理解为指针。第二,并非能以pij这种形式使用,那么p就是一个二维数组了,C标准对数组引用的规定,并没有指定数组引用时运算符的左边必须是数组名,而可以是一个表达式。第三,这是一种巧合,归根到底是由于C语言的数组实现是数组的嵌套同时C标准把运算符转换为类似*(*(a+i)+j)这样的等价表达式造成的,那两个取值运算符恰好可以用于一个二级指针。第四,p与pi并不具有数组类型,sizeof(p)和sizeof(pi)的结果只是一个指针的大小4字节。而对于一个真正的数组,p与pi都是具有数组类型的地址。实际上,int*p只是一个指向一维指针数组的指针,而不是指向二维数组的指针。同样地,对于n级指针,都可以看作一个指向一维指针数组的指针,这个指针数组的元素都是n-1级指针。第六章另类数组动态数组与字符串常量可算是两种另类数组。VLA可变长数组并不为C89所支持,C99才开始支持VLA。但如果想在只支持C89的编译环境中使用VLA的话,怎么办呢?我们可以用动态数组来模拟,动态数组在矩阵的运算中很常见,常用来向函数传递一个大小可变的矩阵。动态数组的原理,是利用一块或多块动态分配的内存存储各维的首地址,这样就可以pij的形式访问数组的数据了。但是,动态数组并非真正的数组,它只是对数组的一种模拟。由于具有数组类型的数组名是系统行为,在用户这一级没法做到,因此只能以指针的形式存放首地址,sizeof(p)和sizeo f(pi)结果都是4字节。虽然动态数组是依靠动态分配内存来建立的,但动态的意义并非来自这里,而是指大小可变。笔者觉得用动态数组这个名称来命名非常适合,既不失大小可变的特征,又可以跟VLA可变长数组区分开来。下面是建立动态数组的示例:#include stdio.h#include stdlib.h void computedata(int*,int,int);int main(void)int iData100,x,y;doprintf(The product obtained by multiplying xand ymust be le ss than 100!);printf(x=);scanf(%d,&x);printf(y=);scanf(%d,&y);while(x*y 100);computedata(iData,x,y);return 0;void computedata(int*ipSource,int iRow,int iColumn)int*ipTemp,i,j;ipTemp=(int*)malloc(iRow*sizeof(int*);for(i=0;i iRow;+i)ipTempi=ipSource+i*iColumn;for(i=0;i iRow;+i)for(j=0;j iColumn;+j)ipTempij+=1;free(ipTemp);return;以上示例把动态数组ipTemp的元素都加了1,由于只是示例,笔者省略了检测数据合法性的代码。iRow是第一维上界,iColumn是第二维上界,iData是源数据缓冲区,iRow*iColumn的积不能超过iData缓冲区的大小,否则就会越界了,但可以比它小。示例中iData被定义为一维数组,当然根据自己的需要也可以用其它类型的缓冲区代替,例如动态分配的一块内存,或者多维数组,如果是多维数组,例如三维数组int iData101010,调用com putedata函数时,实参iData得做一些转换:computedata(int*)iData,x,y);或者computedata(&iData000,x,y);都可以。ipSource指针用来传递缓冲区的首地址,这个指针由于要用来计算各维的地址,因此最好定义为一级指针,这样比较方便。ipTemp是一个二级指针,这是因为它指向的那块内存存放的是指针,这些指针指向各维的首地址,对ipTemp的元素来说,ipTemp就是二级的。最后记得free(ipTemp);以上是定义一个二维动态数组的例子,多维动态数组的创建方法跟这个类似,下面给出三维动态数组的代码:void computedata(int*ipSource,int iHigh,int iRow,int iColumn)int*ipTemp,i,j,k;ipTemp=(int*)malloc(iHigh*sizeof(int*);for(i=0;i iHigh;+i)ipTempi=(int*)malloc(iRow*sizeof(int*);for(i=0;i iHigh;+i)for(j=0;j iRow;+j)ipTempij=ipSource+i*iRow*iColumn+j*iColumn;for(i=0;i iHigh;+i)for(j=0;j iRow;+j)for(k=0;k iColumn;+k)ipTempijk+=1;for(i=0;i iHigh;+i)free(ipTempi);free(ipTemp);return;下面来讨论字符串常量。众所周知,C语言是没有字符串变量的,因而,C89规定,字符串常量就是一个字符数组。因此,尽管字符串常量的外部表现形式跟数组完全不同,但它的确是一个真正的数组,实际上,字符串常量本身就是这个数组的首地址,并且具有数组类型,对一个字符串常量进行sizeof运算,例如sizeof(abcdefghi),结果是10,而不是4。字符串常量与一般数组的主要区别,是字符串常量存放在静态存储区,而一般数组(非static)则是在栈中静态分配的。由于字符串常量是数组首地址,因此可以数组引用的形式使用它,例如:printf(%s,&abcdefghi4);这将打印出字符串efghi。还可以这样:printf(%s,abcdefghi+4);同样打印出字符串efghi。实际上,&abcdefghi4等价于&*(abcdefghi+4),去掉&*后,就是abcdefghi+4了。我们可以利用字符串常量这些特性写出一些有趣的程序来,例如:#include stdio.h int iLine=1;int main(void)printf(%*sn,7-(iLine 4?iLine-4:4-iLine),*+2*(iLine 4?iLine-4:4-i Line);if(+iLine!=8)main();return 0;这个程序不使用任何数组形式的引用,不使用循环,就可以打印出用*号组合出来的菱形。当然,笔者并非鼓励大家编写这样的代码,但通过这样的例子加深对字符串常量的认识,仍然是非常重要的。人们常说,C语言的声明太复杂了,的确,这也是C语言饱受批评的地方之一。不过,笔者认为,真正要受到批评的不是语言本身,而是那些传播者。传播者们通常都有一个共识:讲述要由浅入深。作为原则,笔者并非要反对它,毕竟笔者对C语言的学习,也经历了相同的过程。但是,由浅入深并不意味着一切从简,以偏盖全。计算机语言不同于数学理论(虽然它的确根植于数学,与数学密不可分),数学理论是一种循序渐进的过程,后面的理论以前面的理论为基础。但C语言归根说底,就是一堆语言规则而已,应该让学习者一开始就全面且详细地了解它,而不是象现在某些教材所做的那样,只说一部分,不说另一部分,以为这就是由浅入深了,实际上这是以偏盖全。语言如此,声明作为C语言的一部分更是如此。我们最常见到的对声明的描述是这样的:存储类别类型限定词类型标识符这种说明会给人们一种暗示:C语言的声明是静止的、死板的,什么声明都能够以这个为基础,往上一套就OK了。事实真的如此吗?说句心里话,笔者也祈祷事实真的如此,这样世界就简单多了、清静多了。但别忘了,这个世界总是让人事与愿违的。实际上,C的声明的组织形式是以嵌套为基础的,是用嵌套声明组织起来的,并非象上面所述那么死板,存储类说明符一定得放在限定词前面吗?类型说明符一定要紧贴标识符吗?不!C标准从来没有这样说过!下面来看一看C89对声明的形式是如何规定的:声明:声明说明符初始化声明符表optopt的意思是option,可选其中声明说明符由以下三项构成:声明说明符:存储类说明符声明说明符opt类型说明符声明说明符opt类型限定符声明说明符opt在这里,一个声明说明符可以包含另一个声明说明符,这就是声明的嵌套,这种嵌套贯穿于整个声明之中,今天我们看来一个非常简单的声明,其实就是由多个声明嵌套组成的,例如:static const int i=10,j=20,k=30;变量i前面就是声明说明符部分,有三个声明说明符:static const int,static是一个存储类说明符,它属于这种形式:static声明说明符static后面的声明说明符就是const int,const是一个类型限定符,这也是个嵌套,它是由const声明说明符组成,最后的int是一个类型说明符,到这里已经没有嵌套了,int就是最底的一层。对于存储类说明符、类型说明符和类型限定符的排列顺序,C标准并没有规定其顺序,谁嵌套谁都可以。换言之,上面的声明可以写成:int static const i=10,j=20,k=30;或者const int static i=10,j=20,k=30;这无所谓,跟原声明是一样的。再举一个有趣的例子:const int*p;与int const*p;有些人会对后面一种形式感到困惑,因为他一直以来学习的都是那种死板的形式,因此他无法理解为什么那个const可以放在int的后面。实际上对于标准来说,这是再正常不过的行为了。上面举的例子是变量的声明,函数的声明也同样道理,例如:static const int func(void);.int main(void)int static const(*p)(void);p=func;.return 0;const int static func(void).return 0;func的函数原型声明、函数定义跟main内的函数指针p的声明是一样的。但是,笔者并非鼓励大家把声明说明符写得乱七八糟,作为一个良好的风格,应该按照已经习惯约定的方式排列说明符,但懂得其中的原理非常重要。声明static const int i=10,j=20,k=30;的int后面的部分就是初始化声明符表,这比较容易理解,这个符表实际上也是嵌套的:初始化声明符表:初始化声明符初始化声明符表,初始化声明符初始化声明符:声明符声明符=初值声明符是初始化声明符的主体,现在来讨论一下声明符是如何规定的:声明符:指针opt直接声明符这里写的指针opt指的是那个指针声明符*,要注意的是,*属于声明符,而不是声明说明符的一部分。指针opt又包含:指针:*类型限定符表opt*类型限定符表opt指针在这里有一个常见的问题,就是const int*p;与int*const p的区别,第一个声明的co nst属于声明说明符,它跟int一起,是用来说明*p这个声明符的,因此const修饰的是p所指向的那个对象,这个对象是const的。而第二个声明的const是声明符的一部分,它修饰的对象是p本身,因此p是const的。上面规定的第二条值得注意,这条规定产生了一种指针与const的复杂形式,例如:const int*const*const*const p;(是不是有种想冲向厕所的冲动?)这是一种复杂的声明嵌套,如何解读这种声明?其实只要掌握了它的规律,无论它有多少个const、多少个*都不难解读的,这个内容我将在第九章进行解释。剩下的就是直接声明符和类型限定词表的内容:直接声明符:标识符(声明符)直接声明符常量表达式opt直接声明符(形式参数类型表)直接声明符(标识符表opt)类型限定符表:类型限定符类型限定符表类型限定符这一章的最后一个内容,是讨论一下typedef,typedef用来声明一个别名,type def后面的语法,是一个声明。本来笔者以为这里不会产生什么误解的,但结果却出乎意料,产生误解的人不在少数。罪魁祸首又是那些害人的教材。在这些教材中介绍typedef的时候通常会写出如下形式:typedef int PARA;这种形式跟#define int PARA几乎一样,如前面几章所述,这些教材的宗旨是由浅入深,但实际做出来的行为却是以偏盖全。的确,这种形式在所有形式中是最简单的,但却没有对typedef进一步解释,使得不少人用#define的思维来看待typedef,把int与PARA分开来看,int是一部分,PARA是另一部分,但实际上根本就不是这么一回事。int与PARA是一个整体!就象int i:声明一样是一个整体声明,只不过int i定义了一个变量,而typedef定义了一个别名。这些人由于持有这种错误的观念,就会无法理解如下一些声明:typedef int a10;typedef void(*p)(void);他们会以为a10是int的别名,(*p)(void)是void的别名,但这样的别名看起来又似乎不是合法的名字,于是陷入困惑之中。实际上,上面的语句把a声明为具有10个int元素的数组的类型别名,p是一种函数指针的类型别名。虽然在功能上,typedef可以看作一个跟int PARA分离的动作,但语法上typedef属于存储类声明说明符,因此严格来说,typedef int PARA整个是一个完整的声明。第八章右左法则-复杂指针解析上一章费那么多唇舌讨论C语言的声明,其实目的都是为了这一章,期望读者通过对C语言声明形式的详细了解,树立声明嵌套的观念,因为C语言所有复杂的指针声明,都是由各种声明嵌套构成的。如何解读复杂指针声明呢?右左法则是一个既著名又常用的方法。不过,右左法则其实并不是C标准里面的内容,它是从C标准的声明规定中归纳出来的方法。C标准的声明规则,是用来解决如何创建声明的,而右左法则是用来解决如何辩识一个声明的,两者可以说是相反的。右左法则的英文原文是这样说的:The right-left rule:Start reading the declaration from the innermost parenthe ses,go right,and then go left.When you encounter parentheses,the direction should be reversed.Once everything in the parentheses has been parsed,jump out of it.Continue till the whole declaration has been parsed.这段英文的翻译如下:右左法则:首先从最里面的圆括号看起,然后往右看,再往左看。每当遇到圆括号时,就应该掉转阅读方向。一旦解析完圆括号里面所有的东西,就跳出圆括号。重复这个过程直到整个声明解析完毕。笔者要对这个法则进行一个小小的修正,应该是从未定义的标识符开始阅读,而不是从括号读起,之所以是未定义的标识符,是因为一个声明里面可能有多个标识符,但未定义的标识符只会有一个。现在通过一些例子来讨论右左法则的应用,先从最简单的开始,逐步加深:int(*func)(int*p);首先找到那个未定义的标识符,就是func,它的外面有一对圆括号,而且左边是一个*号,这说明func是一个指针,然后跳出这个圆括号,先看右边,也是一个圆括号,这说明(*fu nc)是一个函数,而func是一个指向这类函数的指针,就是一个函数指针,这类函数具有i nt*类型的形参,返回值类型是(*func)(int*p,int(*f)(int*);func被一对括号包含,且左边有一个*号,说明func是一个指针,跳出括号,右边也有个括号,那么func是一个指向函数的指针,这类函数具有int*和int(*)(int*)这样的形参,返回值为int类型。再来看一看func的形参int(*f)(int*),类似前面的解释,f也是一个函数指针,指向的函数具有int*类型的形参,返回值为(*func5)(int*p);func右边是一个运算符,说明func是一个具有5个元素的数组,func的左边有一个*,说明func的元素是指针,要注意这里的*不是修饰func的,而是修饰func5的,原因是运算符优先级比*高,func先跟结合,因此*修饰的是func5。跳出这个括号,看右边,也是一对圆括号,说明func数组的元素是函数类型的指针,它所指向的函数具有int*类型的形参,返回值类型为(*(*func)5)(int*p);func被一个圆括号包含,左边又有一个*,那么func是一个指针,跳出括号,右边是一个运算符号,说明func是一个指向数组的指针,现在往左看,左边有一个*号,说明这个数组的元素是指针,再跳出括号,右边又有一个括号,说明这个数组的元素是指向函数的指针。总结一下,就是:func是一个指向数组的指针,这个数组的元素是函数指针,这些指针指向具有int*形参,返回值为int类型的函数。int(*(*func)(int*p)5;func是一个函数指针,这类函数具有int*类型的形参,返回值是指向数组的指针,所指向的数组的元素是具有5个int元素的数组。要注意有些复杂指针声明是非法的,例如:int func(void)5;func是一个返回值为具有5个int元素的数组的函数。但C语言的函数返回值不能为数组,这是因为如果允许函数返回值为数组,那么接收这个数组的内容的东西,也必须是一个数组,但C语言的数组名是一个右值,它不能作为左值来接收另一个数组,因此函数返回值不能为数组。int func5(void);func是一个具有5个元素的数组,这个数组的元素都是函数。这也是非法的,因为数组的元素除了类型必须一样外,每个元素所占用的内存空间也必须相同,显然函数是无法达到这个要求的,即使函数的类型一样,但函数所占用的空间通常是不相同的。作为练习,下面列几个复杂指针声明给读者自己来解析,答案放在第十章里。int(*(*func)56)78;int(*(*(*func)(int*)5)(int*);int(*(*func789)(int*)5;实际当中,需要声明一个复杂指针时,如果把整个声明写成上面所示的形式,对程序可读性是一大损害。应该用typedef来对声明逐层分解,增强可读性,例如对于声明:int(*(*func)(int*p)5;可以这样分解:typedef int(*PARA)5;typedef PARA(*func)(int*);这样就容易看得多了。第九章指针与const const一词是英文constant的缩写,设立这个关键字的本意,是希望让它所修饰的对象成为一个常量。记得在国家间的外交中,有一个经常用到的术语:从事与身份不符的活动,这个const恰恰也正从事着这样的活动,呵呵。C语言可以有三种方法定义一个常量:#define、const和枚举,但只有枚举才是真正的常量,什么是真正的常量?真正的常量是没有存储空间的,是一个右值,这意味着通过任何合法的手段也不会被修改,但被const修饰的对象依然是一个左值,尽管这个对象被const限定,笔者仍然至少可以找到三种合法的手段去修改它,而#define所做的只不过是编译期替换而已,只有枚举常量才能真正做到这一点。const实在不应该被命名为const,这会让人们产生误解,它应该命名为readonly或类似的字眼,意即不能通过被const修饰的对象修改它所指向的对象或者它所代表的对象。但在C的世界里把const称为常量早已是普遍的现象,那我们就只好随大流咯,也称之为常量吧,只要知道它实际上不是真正的常量就行了。第七章曾经讨论过const int*p;与int*const p的区别,这两个声明的中文名称常常搞得混乱不堪。第一个声明的const是声明说明符,它修饰p所指向的对象,但p仍然是可变的,这意味着p是一个指向常量的指针,简称常量指针。第二个声明的const是声明符的一部分,它修饰的对象是p,这意味着p是一个常量,而且是一个指针类型的常量,简称指针常量。指针常量又常常被人称为常指针或常指针变量,常指针变量这个名称有点蹩脚,又常又变的,容易让人摸不着头脑,最好还是不要这样称呼。这里还得再强调一次指针常量与地址常量是不同的,不能把数组名称为指针常量,也不能把指针常量称为地址常量,因为指针常量依然是一个左值,而数组名是一个右值,这里肯定有人会问:什么?指针常量是一个左值?我没听错吧?你的确没有听错,C89对于左值是这样定义的:对象是一个命名的存储区域,左值(lvalue)是引用某个对象的表达式。换言之,如果一个表达式引用的是一个具有具体存储空间的对象,它就是一个左值!那么既然指针常量是一个左值,为什么却不能给它赋值呢?是因为它受限于赋值表达式的一条规则:赋值表达式的左值不能含有限定词!为了防止指针指向的常量被修改,C标准对于指针间赋值有一个规定,就是左值必须包含右值的所有限定词。这就限定了一个指向const对象的指针不能赋值给指向非cons t对象的指针,但反过来就允许。这个规定初看上去非常合理,但其效用其实只限于一级指针,二级指针间的赋值即使满足规定也不再安全,下面举个例子:const int i=10;const int*p1;int*p2;p1=&p2;*p1=&i;*p2=20;现在你会发现,作为常量的i的值被修改了。i的值被修改的关键原因在*p1=&i;这一句,&i是一个指向常量的一级地址,如果没有二级指针p1,受限于上述规定,作为左值接受这个一级地址的指针就必须也是一个指向常量的一级指针,于是就不能进行下一步赋值20的操作。因此,正由于指向const对象的二级指针p1的出现,使得*p1也是一个指向const的指针,于是*p1=&i能够合法地运行,常量i的值被修改也就成了一个预想中的结果了。有鉴于此,某些编译器也会限定非const二级指针之间的赋值,规定上面的p1=&p2也是非法的。第七章介绍声明符的指针部分有一种形式:*类型限定符表opt指针这种形式产生了一种比较复杂的带const的指针,例如:const int*const*const*const p;这是一个会让人头晕目眩的表达式,声明符部分嵌套了九次,如何辨认谁是con

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论