当前位置 博文首页 > Jasper6688的博客:C++“指针”学习建议
一.对于众多人提出的c/c++中指针难学的问题做个总结:?
指针学习不好关键是概念不清造成的,说的简单点就是书没有认真看,指针的学习犹如人在学习饶口令不多看多学多练是不行的,下面是两个很经典的例子,很多书上都有,对于学习的重点在于理解*x和x的理解,他们并不相同,*x所表示的其实就是变量a本身,x表示的是变量a在内存中的地址,如果想明白可以输出观察cout<<*x"|"x;,当定义了int *x;后对x=&a的理解的问题。仔细阅读和联系下面的两个例子我想指针问题就不是难点了!?
#include?<stdio.h>? ?
main()?
{?
int?a,b;?/*?定义a,b两个整形变量用于输入两个整数?*/?
int?*point_1,*point_2,*temp_point;?/*?定义三个指针变量?*/?
scanf("%d,%d",&a,&b);?/*?格式化输入a,b的值?*/?
point_1=&a;?/*?把指针变量point_1的值指向变量a的地址?*/?
point_2=&b;?/*?把指针变量point_2的值指向变量b的地址?*/?
if?(a<b)?
{?
????temp_point=point_1;?/*?这里的temp_point是用于临时存储point_1的值也就是变量a的地址的?*/?
????point_1=point_2;?/*?把point_2的值赋予point_1?*/?
????point_2=temp_point;?
????/*?由于point_1的值已经改变无法找到,利用前面临时存储的也就是temp_point找回原point_1的值赋予point_2,打到把point_1和point_2值对换的目的*/?
}?
printf("%d,%d",*point_1,*point_2);?/*?利用*point_1和*point_2也就是分辨指向b和a的方法把值显示屏幕上?*/?
}? ?
/*?此题需要注意和了解是的此法并没有改变变量a,b的值只是利用指针变量分别存储a和b的地址,然后再把那两个指针变量的值对换一下其实就是存储在指针变量里面a与b的地址对换,在利用*point_1和*point_2的方式把调换后的值显示出来这里的*point_1实际就是a,此中算法并非真的改变a,b的值,而是利用指针进行地址交换达到大小排序的目的.?*/?
#include?<stdio.h>?
main()?
{?
int?a,b;?/*?定义a,b两个整形变量用于输入两个整数?*/?
int?*point_1,*point_2;?/*?定义三个指针变量?*/?
scanf("%d,%d",&a,&b);?/*?格式化输入a,b的值?*/?
point_1?=?&a;?/*?把指针变量point_1的值指向变量a的地址?*/?
point_2?=?&b;?/*?把指针变量point_2的值指向变量b的地址?*/?
compositor(point_1,point_2);?/*?调用自定义的排序涵数,把a,b的地址传递给point_1和point_2?*/?
printf("%d,%d",a,b);?/*?打印出a,b的值?*/?
}?
static?compositor(p1,p2)?
int?*p1,*p2;?/*?定义形式参数p1,p2为指针变量?*/?
{?
int?temp;?/*?建立临时存储变量?*/?
????if?(*p1<*p2)?/*?如果*p1<p2,注意这里的*p1和*p2其实就是a和b?*/?
????{?
????????temp?=?*p1;?/*?利用变量temp用于临时存储*p1和就是a的值?*/?
????????*p1?=?*p2;?/*?将*p1的值也就是a的值换成*p2的值也就是b的值,等价于a=b?*/?
????????*p2?=?temp;?/*?将*p2的值也就是temp的值等价于b=temp?*/?
????}?
}? ?
/*?注意:此题与上题不同的是,直接改变了a于b的值达到真实改变的目的?*/
二.C++指针使用方法解惑
“void ClearList(LNode * & HL)”
仔细看一下这种声明方式,确实有点让人迷惑。
下面以void func1(?MYCLASS *&pBuildingElement );?为例来说明这个问题。在某种意义上,"*"和"&"是意思相对的两个东西,把它们放在一起有什么意义呢?。为了理解指针的这种做法,我们先复习一下C/C++编程中无所不在的指针概念。我们都知道MYCLASS*的意思:指向某个对象的指针,此对象的类型为MYCLASS。?Void func1(MYCLASS *pMyClass);?
// 例如:?MYCLASS* p = new MYCLASS;
func1(p);?
上面这段代码的这种处理方法想必谁都用过,创建一个MYCLASS对象,然后将它传入func1函数。现在假设此函数要修改pMyClass:?void func1(MYCLASS *pMyClass)
{
DoSomething(pMyClass);
pMyClass = // 其它对象的指针
}
第二条语句在函数过程中只修改了pMyClass的值。并没有修改调用者的变量p的值。如果p指向某个位于地址0x008a00的对象,当func1返回时,它仍然指向这个特定的对象。(除非func1有bug将堆弄乱了,完全有这种可能。)
现在假设你想要在func1中修改p的值。这是你的权利。调用者传入一个指针,然后函数给这个指针赋值。以往一般都是传双指针,即指针的指针,例如,CMyClass**。
MYCLASS* p = NULL;
func1(&p);
void func1(MYCLASS** pMyClass);
{
*pMyClass = new MYCLASS;
……
}
调用func1之后,p指向新的对象。在COM编程中,你到处都会碰到这样的用法--例如在查询对象接口的QueryInterface函数中:
interface ISomeInterface {
HRESULT QueryInterface(IID &iid, void** ppvObj);?
……
};?
LPSOMEINTERFACE p=NULL;?
pOb->QueryInterface(IID_SOMEINTERFACE, &p);?
此处,p是SOMEINTERFACE类型的指针,所以&p便是指针的指针,在QueryInterface返回的时候,如果调用成功,则变量p包含一个指向新的接口的指针。
如果你理解指针的指针,那么你肯定就理解指针引用,因为它们完全是一回事。如果你象下面这样声明函数:
void func1(MYCLASS *&pMyClass);
{
pMyClass = new MYCLASS;?
……
}
其实,它和前面所讲得指针的指针例子是一码事,只是语法有所不同。传递的时候不用传p的地址&p,而是直接传p本身:
MYCLASS* p = NULL;
func1(p);
在调用之后,p指向一个新的对象。一般来讲,引用的原理或多或少就象一个指针,从语法上看它就是一个普通变量。所以只要你碰到*&,就应该想到**。也就是说这个函数修改或可能修改调用者的指针,而调用者象普通变量一样传递这个指针,不使用地址操作符&。
至于说什么场合要使用这种方法,我会说,极少。MFC在其集合类中用到了它--例如,CObList,它是一个Cobjects指针列表。?
Class CObList : public Cobject {
……
// 获取/修改指定位置的元素
Cobject*& GetAt(POSITION position);
Cobject* GetAt(POSITION position)?const;
};
这里有两个GetAt函数,功能都是获取给定位置的元素。区别何在呢?
区别在于一个让你修改列表中的对象,另一个则不行。所以如果你写成下面这样:?Cobject* pObj = mylist.GetAt(pos);
则pObj是列表中某个对象的指针,如果接着改变pObj的值:?pObj = pSomeOtherObj;
这并改变不了在位置pos处的对象地址,而仅仅是改变了变量pObj。但是,如果你写成下面这样:?Cobject*& rpObj = mylist.GetAt(pos);
现在,rpObj是引用一个列表中的对象的指针,所以当改变rpObj时,也会改变列表中位置pos处的对象地址--换句话说,替代了这个对象。这就是为什么CObList会有两个GetAt函数的缘故。一个可以修改指针的值,另一个则不能。注意我在此说的是指针,不是对象本身。这两个函数都可以修改对象,但只有*&版本可以替代对象。?
在C/C++中引用是很重要的,同时也是高效的处理手段。所以要想成为C/C++高手,对引用的概念没有透彻的理解和熟练的应用是不行的。
?三.数据指针
在C/C++语言中一直是很受宠的;几乎找不到一个不使用指针的C/C++应用。用于存储数据和程序的地址,这是指针的基本功能。用于指向整型数,用整数指针(int*);指向浮点数用浮点数指针(float*);指向结构,用对应的结构指针(struct xxx *);指向任意地址,用无类型指针(void*)。
??? 有时候,我们需要一些通用的指针。在C语言当中,(void*) 可以代表一切;但是在C++中,我们还有一些比较特殊的指针,无法用(void*)来表示。事实上,在C++中,想找到一个通用的指针,特别是通用的函数指针简直是一个“不可能任务”。
???
??? C++是一种静态类型的语言,类型安全在C++中举足轻重。在C语言中,你可以用void*来指向一切;但在C++中,void*并不能指向一切,就算能,也失去了类型安全的意义了。类型安全往往能帮我们找出程序中潜在的一些BUG。
???
??? 下面我们来探讨一下,C++中如何存储各种类型数据的指针。
C++指针探讨?(一)数据指针?? ?? 沐枫网志
???
??? 1.? 数据指针
??? ?数据指针分为两种:常规数据指针和成员数据指针
??? ?
??? 1.1 常规数据指针?
??? ?这个不用说明了,和C语言一样,定义、赋值是很简单明了的。常见的有:int*, double* 等等。
??? ?如:
?????int?value?=?123;
?????int?*?pn?=?&value;
??? ??? ?
??? 1.2 成员数据指针
??? ?有如下的结构:
?????struct?MyStruct
?????{
???????int?key;
???????int?value;
?????};
???
??? ?现在有一个结构对象:
?????MyStruct?me;
???? MyStruct* pMe = &me;
???
??? ?我们需要?value 成员的地址,我们可以:
?????int?*?pValue?=?&me.value;
???? //或
???? int?* pValue?= &pMe->value;?
???
???? 当然了,这个指针仍然是属于第一种范筹----常规数据指针。
??? ?
??? ?好了,我们现在需要一种指针,它指向MyStruct中的任一数据成员,那么它应该是这样的子:
?????int?MyStruct::*?pMV?=?&MyStruct::value;
?????//或
?????int?MyStruct::*?pMK?=?&MyStruct::key;
???
??? ?这种指针的用途是用于取得结构成员在结构内的地址。我们可以通过该指针来访问成员数据:
?????int?value?=?pMe->*pMV;?//?取得pMe的value成员数据。
?????int?key =?me.*pMK;?//?取得me的key成员数据。
???
???? 那么,在什么场合下会使用到成员数据指针呢?
??? 确实,成员指针本来就不是一种很常用的指针。不过,在某些时候还是很有用处的。我们先来看看下面的一个函数:
??int?sum(MyStruct* objs,?int?MyStruct::*?pm,?int?count)
??{
??????int?result?=?0;
??????for(int?i?=?0;?i?<?count;?++i)
??????????result?+=?objs[i].*pm;
??????return?result;
??}
??? ?这个函数的功能是什么,你能看明白吗?它的功能就是,给定count个MyStruct结构的指针,计算出给定成员数据的总和。有点拗口对吧?看看下面的程序,你也许就明白了:
??? ?
?????MyStruct?me[10]?=
?????{
??????{1,2},{3,4},{5,6},{7,8},{9,10},{11,12},{13,14},{15,16},{17,18},{19,20}
?????};?????
?????int?sum_value?=?sum(me,?&MyStruct::value,?10);
?????//计算10个MyStruct结构的value成员的总和:?sum_value?值?为?110?????(2+4+6+8++20)
?????int?sum_key?=?sum(me,?&MyStruct::key,?10);
???//计算10个MyStruct结构的key成员的总和:???sum_key?值?为?100???????(1+3+5+7++19)
??? ?也许,你觉得用常规指针也可以做到,而且更易懂。Ok,没问题:
?????int?sum(MyStruct* objs,?int?count)
?????{
??????int?result?=?0;
??????for(int?i?=?0;?i?<?count;?++i)
???????result?+=?objs[i].value;
??????return?result;
?????}
你是想这么做吗?但这么做,你只能计算value,如果要算key的话,你要多写一个函数。有多少个成员需要计算的话,你就要写多少个函数,多麻烦啊。指针
在下列函数声明中,为什么要同时使用*和&符号?以及什么场合使用这种声明方式? ?