C++从零开始之指针及其语义和应用[VC/C++编程]
本文“C++从零开始之指针及其语义和应用[VC/C++编程]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
本篇是《C++从零开始》系列的附篇.因友人频频认为《C++从零开始》系列中对指针的阐述太过简单,而提出的各个概念又杂七混八,且关于指针这一C++中的重要概念的应用少之又少,故本篇重点阐明在《C++从零开始》系列中提出的数字、地址、指针等底子概念,并给出指针的语义,阐明指针和数组的关系,阐述多级指针、多维数组、函数指针、数组指针、成员指针的语义及各自的应用.
数字、操作符、范例、范例修饰符
在《C++从零开始(三)》中已经阐明,其实CPU连二进制数都不熟习,其只能处理状况,而它能处理的状况刚好能用二进制数表示,故称CPU只熟习二进制数.应注意由于CPU熟习二进制数是熟习其所表示的状况,并非数学意义上的二进制数,因此CPU并不熟习十进制数20.不过将20按数学法则转成二进制数10100后,运气极好地CPU的计划人员将加法指令定义成状况10100和状况10100相加将得到状况101000,而这个二进制数按数学法则转成十进制数恰好是40,即CPU连加减乘除都不会,只会以差别的方法改变状况,而CPU的计划人员专门将那些状况的改变方法定义成和数学上的加减乘除一样进而使CPU表现得仿佛会加减乘除.
所以,为了让CPU履行一条指令,则那条指令所触及的东西只能是二进制数,就必须有法则将给出的数学意义上的数转换成二进制数.如前面的十进制转二进制的法则,在《C++从零开始(二)》中提到的原码、补码、IEEE real*4等.而要编写C++代码,则一定要在代码中能表现上述的转换法则,并且要能在代码上表现欲被转换的数学意义上的数,这样编译器才能按照我们书写的代码来决意CPU要操作的东西的二进制表示.对此,C++顶用范例表现前者,用数字表现后者,用操作符表示CPU的指令,即CPU状况的变更方法.
因此,为了让CPU履行加法指令,代码上书写加法指令对应的操作符——“+”,“+”的两侧需求接两个数字,而数字的范例决意了若何将数字所表示的数学上的数转换成二进制数.应注意数字是编译级的概念,不是代码级的概念,即无法在代码上表现数字,而只能通过操作符的计算的返回来得到数字.因为任何操作符都要返回数字(不返回数字的操作符也可以通过返回范例为void的数字来表示以满意这一说法),而最常见的一种得到数字的操作符就是普通被称作常数的东西,如6.3、5.2f、0772等.我在《C++从零开始(二)》中将其称作数字的确惹起概念混合,在此特澄清.
应注意只如果返回数字的东西就是操作符,故前面的常量也是一种操作符.关于变量、成员变量及函数,在《C++从零开始》系列中已多次夸大它们都是映射元素,直接书写变量名、成员变量名和函数名将辨别返回各自所映射的数字,即变量名函数名等也都是操作符.
数字是具有范例的,C++供应了自定义范例struct、class等来自定义复杂的范例,但不但如此,C++还供应了更值得赞誉的东西——范例修饰符.在《C++从零开始(五)》中已经阐明,范例修饰符就是修饰范例用的,即按某种法则改变被修饰范例(称作原范例)所表征的数字转换法则.如猪血羊血和猪肉羊肉,这里的“血”和“肉”都是范例修饰符,改变其各自的原范例——“猪”和“羊”.上面感受更像后者修饰前者而非前者修饰后者,如猪血中的“血”是主语而“猪”是定语.即范例修饰符其实是以原范例的信息来改正其自身所表征的那个数字转换法则.这就如称“血”、“肉”是一种东西一样,也说某范例是指针范例、引用范例、数组范例、函数范例等.
在《C++从零开始》系列中共提出下面几种范例修饰符——引用“&”、指针“*”、数组“[]”、函数“()”、函数调用法则“__stdcall”、偏移“<自定义范例名>::”、常量“const”和地址范例修饰符.此中的地址范例修饰符是最混乱的.
在《C++从零开始(三)》中已经阐明地址在32位操作系统中就是一个数,这个数常常以32位长的二进制数表示,以唯一标识一特定内存单元.而一个数字的范例是地址范例时(因为有地址范例修饰符,就仿佛一个数字是数组范例时),就将这个数字所代表的数学意义上的数用二进制表示,以标识出一个内存单元,然后按照原范例的法则来注释那块内存单元及后来续单元的内容(范例的长度大概不止一个字节,而地址范例是范例修饰符,故一定有原范例).由于变量映射的数实际是地址,故变量所映射的数字就是地址范例的.如long a;,假定a映射的是3006,当书写a = 3;时,由于a是变量名,故返回a所映射的数字3006,范例是long范例的地址范例.由于是地址范例,“=”操作符的语法查抄成功(这是范例的另一个用处——语法查抄,就仿佛动名描述词一样),履行“=”操作符的计算.
应注意C++并未提出地址范例修饰符这个概念,只是我出于语法上的完备而提出的,不然要触及更多的无谓概念和法则,如*( p + 1 ) = 20; a[2] = 3;等的注释将复杂化,故在《C++从零开始》系列中提出地址范例的数字这个概念,旨在以尽大概少的概念注释尽大概多的语法.
最常用的范例修饰符——指针和数组
在《C++从零开始(五)》中已阐明指针只是一种范例修饰符.一个数字是指针范例时,将这个数字所代表的数学意义上的数用二进制表示并返回.前面已说过数字的用处就是转换其代表的数为二进制数,其范例仅阐明若何转换,而指针范例所代表的法则就是按数学法则变成二进制数,而不管其原范例是何种范例.由于不受原范例的影响,故指针范例老是固定长度(对成员指针则不一定),在32位操作系统上是四个字节.
如long a; long *p = &a;.假定a映射的是3006,p映射的是3010.关于*p = 3;,p这个操作符返回范例为long的指针范例的地址范例的数字3010,即这个数字的范例被两个范例修饰符两次修饰,由于最后是被地址修饰,故3010是地址范例的数字,而其原范例是long的指针范例.故*p返回范例为long范例的地址范例的数字3006,然后履行“=”操作符的计算.
这里请注意两个操作符——取内容操作符“*”和取地址操作符“&”.在《C++从零开始(五)》中也夸大过,它们其实名不副实,应当叫范例转换操作符才对.即前者后接指针范例的数字,返回的数字原封不动,仅将其范例变成地址范例;后者后接地址范例的数字,返回的数字原封不动,仅将其范例变成指针范例.这有点耍小聪明的感受,但请注意:long *p1 = 0; long *p2 = &*p1;假如“*”的操作是取内容,则&*p1将先取地址为0的内存单元的内容,这将惹起内存拜候违规,但实际并不会,因为“*”仅转换范例而非取内容(取内容是由地址范例的数字的计算来实现的).
前面已阐明,在指针范例的数字返回二进制数时,并不需求原范例的参与,即范例为long*的数字3006和范例为char*的数字3006返回的二进制数都一样,都是3006对应的二进制数.那么为什么要让指针是范例修饰符以带个不用的原范例?按照前面便可看出指针范例的原范例是给取内容操作符“*”用的.但它还有个用处,因为数组范例修饰符的加入,使得指针多了一个所谓的运算功效,在此先看看数组范例.
在《C++从零开始(五)》中已具体阐明数组的修饰功效是将原范例的元素反复多个持续存放以此形成一个新的范例.如long a[10];,a的范例就是long[10],长度为10*sizeof(long)=40个字节,而char[7]范例所对应的长度就是7*sizeof(char)=7个字节.一个数字的范例是数组范例时,因这个数字的长度可一个字节,可一万个字节,故这个数字一定被存放在某块内存中,而数组范例的数字返回的二进制数就是其被存放的内存的首地址.所从前面提到的常数就不能返回一个数组范例的数字,因其没有给出一块内存来存放数组范例的数字.
这里有点混乱,注意数字不一定非要被内存所存储.关于long a[3] = { 45, 45, 45 };,假定a映射的数字是3000,则表示以long[3]的法则注释内存单元3000所记录的数字,这个数字的长度是3*sizeof(long)=12个字节,它的值由于数组范例是长度可变的而决意利用3000(记录它的内存的地址)来代表它,实际是3个值为45的数.所以a;将先返回long[3]范例的地址范例的数字3000,然后计算此地址范例的数字而返回其原范例的数字,由于原范例是long[3],而这个数字存放在3000所标识的内存处,故最后返回3000所对应的二进制数.
简单发现指针返回的是一个地址,数组也是一个地址,当它们原范例相同时,后者可以隐式范例转换为前者,但反之不行,因为数组还具有元素个数这个信息,即long[2]和long[3]的原范例相同,但范例差别.因此有:long a[3]; long *p = a;.这里没任何问题,假定a映射的是3000,则p的值就是3000.因此*p = 3;就是将3放到3000处存放的数组范例的数字的第0个元素中.为了放到第1个和第2个元素中,C++供应了一个所谓的指针运算功效,以下:*( p + 1 ) = 4; *( p + 2 ) = 5;.这里就把4放到第1个元素,5放到第2个元素中.对*( p + 1 ) = 4;,p返回一个long*的数字3000,而p + 1返回long*的数字3004,然后持续后续计算.同理,p + 2返回范例为long*的数字3000+2*sizeof(long)=3008.即指针只能举行整数加减,如:char *p1 = 0; p1++; p1 = p1 + 5 * 8 - 1; short *p2 = 0; p2 += 11; p2——;上面p1的值为40,p2的值也为40,因为p1的原范例是char而p2的是short.因此为了得到数组的第2个元素的值,需*( p + 2 );,这很明显地不便于阅读,为此C++专门供应了一个下标操作符“[]”,其前面接指针范例的数字,方括号中放一整型数字,将指针范例的数字换成地址范例,再将值按前面提到的指针运算法则变更,返回.如long a[4]; long *p = a;,假定a映射的是3000.则a[2] = 1;等效于*( p + 2 ) = 1;,a[2]前面接的是long*范例的数字3000(隐式范例转换,从long[4]转成long*),加2*sizeof(long),返回3008,范例则简单地变成long范例的地址范例.由于“[]”仅仅只是前面说的指针运算的简化易读版本,故也可a[-1] = 3;,其等效于*( p - 1 ) = 3;.由于“[]”前接指针,故也可p[-1] = 3;,等效于a[-1] = 3;.
算法则变更,返回.如long a[4]; long *p = a;,假定a映射的是3000.则a[2] = 1;等效于*( p + 2 ) = 1;,a[2]前面接的是long*范例的数字3000(隐式范例转换,从long[4]转成long*),加2*sizeof(long),返回3008,范例则简单地变成long范例的地址范例.由于“[]”仅仅只是前面说的指针运算的简化易读版本,故也可a[-1] = 3;,其等效于*( p - 1 ) = 3;.由于“[]”前接指针,故也可p[-1] = 3;,等效于a[-1] = 3;.
以上是“C++从零开始之指针及其语义和应用[VC/C++编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
本文地址: | 与您的QQ/BBS好友分享! |