马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
现在的年轻人,清一色的追求看书看国外教材,当然我也不能低人一个档次,看的都是有名的大师作品,不期自己能够编出惊天地泣鬼神的大作。 指针是一个特别的变量,它外面存储的数值被注释成为内存里的一个地址。要弄清一个指针需求弄清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或叫指针所指向的内存区,还有指针自己所占有的内存区。让咱们分离申明。
先声明几个指针放着做例子:
例一:
- int *ptr; char *ptr; int **ptr; int (*ptr)[3]; int *(*ptr)[4];
复制代码 指针的类型
从语法的角度看,你只需把指针声明语句里的指针名字去失落,剩下的局部就是这个指针的类型。这是指针自己所具有的类型。让咱们看看例一中各个指针的类型:
- int *ptr; //指针的类型是int * char *ptr; //指针的类型是char * int **ptr; //指针的类型是 int ** int (*ptr)[3]; //指针的类型是 int(*)[3] int *(*ptr)[4]; //指针的类型是 int *(*)[4]
复制代码 怎样?找出指针的类型的办法是否是很复杂?
指针所指向的类型
当你经由过程指针来会见指针所指向的内存区时,指针所指向的类型决意了编译器将把那片内存区里的内容当作甚么来对待。
从语法上看,你只须把指针声明语句中的指针名字和名字右边的指针声明符*去失落,剩下的就是指针所指向的类型。例如:
- int *ptr; //指针所指向的类型是int char *ptr; //指针所指向的的类型是char int **ptr; //指针所指向的的类型是 int * int (*ptr)[3]; //指针所指向的的类型是 int()[3] int *(*ptr)[4]; //指针所指向的的类型是 int *()[4]
复制代码 在指针的算术运算中,指针所指向的类型有很大的感化。
指针的类型(即指针自己的类型)和指针所指向的类型是两个概念。当你对C愈来愈熟习时,你会发明,把与指针搅和在一同的“类型”这个概念分红“指针的类型”和“指针所指向的类型”两个概念,是精晓指针的关头点之一。我看了很多书,发明有些写得差的书中,就把指针的这两个概念搅在一同了,所以看起书来前后抵触,越看越懵懂。
指针的值
指针的值是指针自己存储的数值,这个值将被编译器看成一个地址,而不是一个普通的数值。在32位法式里,一切类型的指针的值都是一个32位整数,由于32位法式里内存地址全都是32位长。
指针所指向的内存区就是从指针的值所代表的谁人内存地址入手下手,长度为sizeof(指针所指向的类型)的一片内存区。今后,咱们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;咱们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。
指针所指向的内存区和指针所指向的类型是两个完整分歧的概念。在例一中,指针所指向的类型已有了,但因为指针还未初始化,所以它所指向的内存区是不存在的,或说是有意义的。
今后,每碰到一个指针,都应当问问:这个指针的类型是甚么?指针指向的类型是甚么?该指针指向了哪里?
指针自己所占有的内存区
指针自己占了多大的内存?你只需用函数sizeof(指针的类型)测一下就晓得了。在32位平台里,指针自己占有了4个字节的长度。
指针自己占有的内存这个概念在判别一个指针表达式是不是是左值时很有效。
指针的算术运算
指针可以加上或减去一个整数。指针的这类运算的意义和凡是的数值的加减运算的意义是纷歧样的。例如:
例二:
- char a[20]; int *ptr=a; ... ... ptr++;
复制代码 在上例中,指针ptr的类型是int*,它指向的类型是int,它被初始化为指向整形变量a。接上去的第3句中,指针ptr被加了1,编译器是如许处置的:它把指针ptr的值加上了sizeof(int),在32位法式中,是被加上了4。因为地址是用字节做单元的,故ptr所指向的地址由本来的变量a的地址向洼地址偏向增添了4个字节。
因为char类型的长度是一个字节,所以,本来ptr是指向数组a的第0号单位入手下手的四个字节,此时指向了数组a中从第4号单位入手下手的四个字节。
咱们可以用一个指针和一个轮回来遍历一个数组,看例子:
例三:
- int array[20]; int *ptr=array; ... //此处略去为整型数组赋值的代码。 ... for(i=0;i<20;i++) { (*ptr)++; ptr++; }
复制代码 这个例子将整型数组中各个单位的值加1。因为每次轮回都将指针ptr加1,所以每次轮回都能会见数组的下一个单位。再看例子:
例四:
- char a[20]; int *ptr = a; ... ... ptr += 5;
复制代码 在这个例子中,ptr被加上了5,编译器是如许处置的:将指针ptr的值加上5乘sizeof(int),在32位法式中就是加上了5乘4=20。因为地址的单元是字节,故如今的ptr所指向的地址比起加5后的ptr所指向的地址来讲,向洼地址偏向挪动了20个字节。在这个例子中,没加5前的ptr指向数组a的第0号单位入手下手的四个字节,加5后,ptr已指向了数组a的正当局限以外了。固然这类情形在使用上会出成绩,但在语法上倒是可以的。这也表现出了指针的天真性。
假如上例中,ptr是被减去5,那末处置进程迥然不同,只不外ptr的值是被减去5乘sizeof(int),新的ptr指向的地址将比本来的ptr所指向的地址向低地址偏向挪动了20个字节。
总结一下,一个指针ptrold加上一个整数n后,了局是一个新的指针ptrnew,ptrnew的类型和ptrold的类型不异,ptrnew所指向的类型和ptrold所指向的类型也不异。ptrnew的值将比ptrold的值增添了n乘sizeof(ptrold所指向的类型)个字节。就是说,ptrnew所指向的内存区将比ptrold所指向的内存区向洼地址偏向挪动了n乘sizeof(ptrold所指向的类型)个字节。一个指针ptrold减去一个整数n后,了局是一个新的指针ptrnew,ptrnew的类型和ptrold的类型不异,ptrnew所指向的类型和ptrold所指向的类型也不异。ptrnew的值将比ptrold的值削减了n乘sizeof(ptrold所指向的类型)个字节,就是说,ptrnew所指向的内存区将比ptrold所指向的内存区向低地址偏向挪动了n乘sizeof(ptrold所指向的类型)个字节。
运算符&和*
这里&是取地址运算符,*是…书上叫做“直接运算符”。&a的运算了局是一个指针,指针的类型是a的类型加个*,指针所指向的类型是a的类型,指针所指向的地址嘛,那就是a的地址。*p的运算了局就八门五花了。总之*p的了局是p所指向的器材,这个器材有这些特色:它的类型是p指向的类型,它所占用的地址是p所指向的地址。
例五:
- int a=12; int b; int *p; int **ptr; p=&a;//&a的了局
是一个指针,类型是int*,指向的类型是int,指向的地址是a的地址。 *p=24;//*p的了局
,在这里它的类型是int,它所占用的地址是p所指向的地址,明显
,*p就是变量a。 ptr=&p;//&p的了局
是个指针,该指针的类型是p的类型加个*,在这里是int**。该指针所指向的类型是p的类型,这里是int*。该指针所指向的地址就是指针p本人
的地址。 *ptr=&b;//*ptr是个指针,&b的了局
也是个指针,且这两个指针的类型和所指向的类型是一样的,所以?amp;b来给*ptr赋值就是毫无成绩
的了。 **ptr=34;//*ptr的了局
是ptr所指向的器材
,在这里是一个指针,对这个指针再做一次*运算,了局
就是一个int类型的变量。
复制代码 指针表达式
一个表达式的最初了局假如是一个指针,那末这个表达式就叫指针表达式。上面是一些指针表达式的例子:
例六:
- int a,b; int array[10]; int *pa; pa=&a;//&a是一个指针表达式。 int **ptr=&pa;//&pa也是一个指针表达式。 *ptr=&b;//*ptr和&b都是指针表达式。 pa=array; pa++;//这也是指针表达式。
复制代码 例七:
- char *arr[20]; char **parr=arr;//假如
把arr看做
指针的话,arr也是指针表达式 char *str; str=*parr;//*parr是指针表达式 str=*(parr+1);//*(parr+1)是指针表达式 str=*(parr+2);//*(parr+2)是指针表达式
复制代码 因为指针表达式的了局是一个指针,所以指针表达式也具有指针所具有的四个要素:指针的类型,指针所指向的类型,指针指向的内存区,指针本身占有的内存。
好了,当一个指针表达式的了局指针已明白地具有了指针本身占有的内存的话,这个指针表达式就是一个左值,不然就不是一个左值。 在例七中,&a不是一个左值,由于它还没有占有明白的内存。*ptr是一个左值,由于*ptr这个指针已占有了内存,其实*ptr就是指针pa,既然pa已在内存中有了本人的地位,那末*ptr固然也有了本人的地位。
数组和指针的关系
假如对声明数组的语句不太分明的话,请参阅我前段工夫贴出的文章<<若何了解c和c++的庞杂类型声明>>。 数组的数组名其实可以看做一个指针。看下例:
例八:
- int array[10]={0,1,2,3,4,5,6,7,8,9},value; ... ... value=array[0];//也可写成:value=*array; value=array[3];//也可写成:value=*(array+3); value=array[4];//也可写成:value=*(array+4);
复制代码 上例中,普通而言数组名array代表数组自己,类型是int [10],但假如把array看作指针的话,它指向数组的第0个单位,类型是int *,所指向的类型是数组单位的类型即int。因而*array等于0就一点也不奇异了。同理,array+3是一个指向数组第3个单位的指针,所以*(array+3)等于3。其它依此类推。
例九:
- int *ptr; //指针的类型是int * char *ptr; //指针的类型是char * int **ptr; //指针的类型是 int ** int (*ptr)[3]; //指针的类型是 int(*)[3] int *(*ptr)[4]; //指针的类型是 int *(*)[4]0
复制代码 上例中,str是一个三单位的数组,该数组的每一个单位都是一个指针,这些指针各指向一个字符串。把指针数组名str看成一个指针的话,它指向数组的第0号单位,它的类型是char**,它指向的类型是char *。
*str也是一个指针,它的类型是char*,它所指向的类型是char,它指向的地址是字符串”Hello,this is a sample!”的第一个字符的地址,即’H'的地址。 str+1也是一个指针,它指向数组的第1号单位,它的类型是char**,它指向的类型是char *。
*(str+1)也是一个指针,它的类型是char*,它所指向的类型是char,它指向”Hi,good morning.”的第一个字符’H',等等。
上面总结一下数组的数组名的成绩。声了然一个数组TYPE array[n],则数组称号array就有了两重寄义:第一,它代表全部数组,它的类型是TYPE [n];第二,它是一个指针,该指针的类型是TYPE*,该指针指向的类型是TYPE,也就是数组单位的类型,该指针指向的内存区就是数组第0号单位,该指针本人占领独自的内存区,注重它和数组第0号单位占有的内存区是分歧的。该指针的值是不克不及修正的,即相似array++的表达式是毛病的。
在分歧的表达式中数组名array可以饰演分歧的脚色。
在表达式sizeof(array)中,数组名array代表数组自己,故这时候sizeof函数测出的是全部数组的巨细。
在表达式*array中,array饰演的是指针,因而这个表达式的了局就是数组第0号单位的值。sizeof(*array)测出的是数组单位的巨细。
表达式array+n(个中n=0,1,2,….。)中,array饰演的是指针,故array+n的了局是一个指针,它的类型是TYPE*,它指向的类型是TYPE,它指向数组第n号单位。故sizeof(array+n)测出的是指针类型的巨细。
例十:
- int *ptr; //指针的类型是int * char *ptr; //指针的类型是char * int **ptr; //指针的类型是 int ** int (*ptr)[3]; //指针的类型是 int(*)[3] int *(*ptr)[4]; //指针的类型是 int *(*)[4]1
复制代码 上例中ptr是一个指针,它的类型是int (*)[10],他指向的类型是int [10],咱们用全部数组的首地址来初始化它。在语句ptr=&array中,array代表数组自己。
本节中提到了函数sizeof(),那末我来问一问,sizeof(指针称号)测出的事实是指针本身类型的巨细呢仍是指针所指向的类型的巨细?谜底是前者。例如:
- int *ptr; //指针的类型是int * char *ptr; //指针的类型是char * int **ptr; //指针的类型是 int ** int (*ptr)[3]; //指针的类型是 int(*)[3] int *(*ptr)[4]; //指针的类型是 int *(*)[4]2
复制代码 则在32位法式中,有:
- int *ptr; //指针的类型是int * char *ptr; //指针的类型是char * int **ptr; //指针的类型是 int ** int (*ptr)[3]; //指针的类型是 int(*)[3] int *(*ptr)[4]; //指针的类型是 int *(*)[4]3
复制代码 实践上,sizeof(对象)测出的都是对象本身的类型的巨细,而不是其余甚么类型的巨细。
指针和布局类型的关系
可以声明一个指向布局类型对象的指针。
例十一:
- int *ptr; //指针的类型是int * char *ptr; //指针的类型是char * int **ptr; //指针的类型是 int ** int (*ptr)[3]; //指针的类型是 int(*)[3] int *(*ptr)[4]; //指针的类型是 int *(*)[4]4
复制代码 请问如何经由过程指针ptr来会见ss的三个成员变量?
谜底:
- int *ptr; //指针的类型是int * char *ptr; //指针的类型是char * int **ptr; //指针的类型是 int ** int (*ptr)[3]; //指针的类型是 int(*)[3] int *(*ptr)[4]; //指针的类型是 int *(*)[4]5
复制代码 又请问如何经由过程指针pstr来会见ss的三个成员变量?
谜底:
- int *ptr; //指针的类型是int * char *ptr; //指针的类型是char * int **ptr; //指针的类型是 int ** int (*ptr)[3]; //指针的类型是 int(*)[3] int *(*ptr)[4]; //指针的类型是 int *(*)[4]6
复制代码 呵呵,固然我在我的MSVC++6.0上调式过上述代码,然而要晓得,如许利用pstr来会见布局成员是不正轨的,为了申明为何不正轨,让咱们看看如何经由过程指针来会见数组的各个单位:
例十二:
- int *ptr; //指针的类型是int * char *ptr; //指针的类型是char * int **ptr; //指针的类型是 int ** int (*ptr)[3]; //指针的类型是 int(*)[3] int *(*ptr)[4]; //指针的类型是 int *(*)[4]7
复制代码 经由过程指针pa会见数组array的三个单位的办法是:
- int *ptr; //指针的类型是int * char *ptr; //指针的类型是char * int **ptr; //指针的类型是 int ** int (*ptr)[3]; //指针的类型是 int(*)[3] int *(*ptr)[4]; //指针的类型是 int *(*)[4]8
复制代码 从格局上看却是与经由过程指针会见布局成员的不正轨办法的格局一样。
一切的C/C++编译器在分列数组的单位时,老是把各个数组单位寄存在一连的存储区里,单位和单位之间没有空地。但在寄存布局对象的各个成员时,在某种编译情况下,能够会需求字对齐或双字对齐或是其余甚么对齐,需求在相邻两个成员之间加若干个“填充字节”,这就招致各个成员之间能够会有若干个字节的空地。
所以,在例十二中,即便*pstr会见到了却构对象ss的第一个成员变量a,也不克不及包管*(pstr+1)就必定能会见到布局成员b。由于成员a和成员b之间能够会有若干填充字节,说不定*(pstr+1)就正好会见到了这些填充字节呢。这也证实了指针的天真性。如果你的目标就是想看看各个布局成员之间究竟有无填充字节,嘿,这却是个不错的办法。
经由过程指针会见布局成员的准确办法应当是象例十二中利用指针ptr的办法。
指针和函数的关系
可以把一个指针声明成为一个指向函数的指针。
- int *ptr; //指针的类型是int * char *ptr; //指针的类型是char * int **ptr; //指针的类型是 int ** int (*ptr)[3]; //指针的类型是 int(*)[3] int *(*ptr)[4]; //指针的类型是 int *(*)[4]9
复制代码 可以把指针作为函数的形参。在函数挪用语句中,可以用指针表达式来作为实参。
有首歌曲这样唱:说到不如做到,要做就做最好。 |