以前也学习了指针,用了指针,但是都不是很深入,今天就犯了一个错误,为了这个错误,专门写这篇文章,避免日后重犯。
typedef CArrayTemplate<tagOutCardResult *> tagOutCardResultAry; //扑克数据
//出牌结果
struct tagOutCardResult
{
BYTE cbCardCount; //扑克数目
BYTE cbResultCard[MAX_COUNT]; //结果扑克
};
//出牌搜索
bool CGameLogic::GetERRCardAry(const BYTE cbHandCardData[], BYTE cbHandCardCount,
tagOutCardResultAry & m_OutCardResultAry)
{
//设置结果
ZeroMemory(&m_OutCardResultAry,sizeof(m_OutCardResultAry));
//构造扑克
BYTE cbCardData[MAX_COUNT];
BYTE cbCardCount=cbHandCardCount;
CopyMemory(cbCardData,cbHandCardData,sizeof(BYTE)*cbHandCardCount);
//数目过虑
if (cbCardCount==0) return false;
//转换数值
BYTE cbSortValue[MAX_COUNT];
for (BYTE i=0;i<cbCardCount;i++) cbSortValue[i]=GetCardLogicValue(cbCardData[i]);
//排序操作
bool bSorted=true;
BYTE cbThreeCount,cbLast=cbCardCount-1;
do
{
bSorted=true;
for (BYTE i=0;i<cbLast;i++)
{
if ((cbSortValue[i]<cbSortValue[i+1])||
((cbSortValue[i]==cbSortValue[i+1])&&(cbCardData[i]<cbCardData[i+1])))
{
//交换位置
cbThreeCount=cbCardData[i];
cbCardData[i]=cbCardData[i+1];
cbCardData[i+1]=cbThreeCount;
cbThreeCount=cbSortValue[i];
cbSortValue[i]=cbSortValue[i+1];
cbSortValue[i+1]=cbThreeCount;
bSorted=false;
}
}
cbLast--;
} while(bSorted==false);
//分析扑克
BYTE cbIndex=0;
tagAnalyseResult AnalyseResult;
AnalysebCardData(cbCardData,cbCardCount,AnalyseResult);
//拷贝四牌
CopyMemory(&cbCardData[cbIndex],AnalyseResult.cbFourCardData,sizeof(BYTE)*AnalyseResult.cbFourCount*4);
cbIndex+=AnalyseResult.cbFourCount*4;
//拷贝三牌
CopyMemory(&cbCardData[cbIndex],AnalyseResult.cbThreeCardData,sizeof(BYTE)*AnalyseResult.cbThreeCount*3);
cbIndex+=AnalyseResult.cbThreeCount*3;
//拷贝对牌
CopyMemory(&cbCardData[cbIndex],AnalyseResult.cbDoubleCardData,sizeof(BYTE)*AnalyseResult.cbDoubleCount*2);
cbIndex+=AnalyseResult.cbDoubleCount*2;
//拷贝单牌
CopyMemory(&cbCardData[cbIndex],AnalyseResult.cbSignedCardData,sizeof(BYTE)*AnalyseResult.cbSignedCount);
cbIndex+=AnalyseResult.cbSignedCount;
BYTE CardResultAryIndex = 0;
tagOutCardResult* pOutCardResult = new tagOutCardResult();
ZeroMemory(pOutCardResult,sizeof(pOutCardResult));
if (AnalyseResult.cbSignedCount>0)
{
//设置结果
pOutCardResult->cbCardCount=1;
for (BYTE j=0;j<AnalyseResult.cbSignedCount;j++)
{
pOutCardResult->cbResultCard[0]=cbCardData[cbCardCount-1-j];//倒序填充正序
CString strCardData;
strCardData.Format("pOutCardResult->cbResultCard[0] is %d",pOutCardResult->cbResultCard[0]);
WriteLog(strCardData);
m_OutCardResultAry.Add(pOutCardResult);
}
}
CardResultAryIndex = AnalyseResult.cbSignedCount;
if (AnalyseResult.cbDoubleCount>0)
{
//设置结果
BYTE i= 0;
pOutCardResult->cbCardCount=2;
for (BYTE j=0;j<AnalyseResult.cbDoubleCount;j++)
{
pOutCardResult->cbResultCard[0]=cbCardData[cbCardCount-1-i-CardResultAryIndex];
pOutCardResult->cbResultCard[1]=cbCardData[cbCardCount-1-i-CardResultAryIndex-1];
i+=2;
CString strCardData;
strCardData.Format("pOutCardResult->cbResultCard[0] is %d",pOutCardResult->cbResultCard[0]);
WriteLog(strCardData);
strCardData.Format("pOutCardResult->cbResultCard[1] is %d",pOutCardResult->cbResultCard[1]);
WriteLog(strCardData);
m_OutCardResultAry.Add(pOutCardResult); //加入vector中
}
}
CardResultAryIndex += AnalyseResult.cbDoubleCount;
if (AnalyseResult.cbThreeCount>0)
{
//设置结果
BYTE i= 0;
pOutCardResult->cbCardCount=3;
for (BYTE j=0;j<AnalyseResult.cbThreeCount;j++)
{
pOutCardResult->cbResultCard[0]=cbCardData[cbCardCount-1-i-CardResultAryIndex];
pOutCardResult->cbResultCard[1]=cbCardData[cbCardCount-1-i-CardResultAryIndex-1];
pOutCardResult->cbResultCard[2]=cbCardData[cbCardCount-1-i-CardResultAryIndex-2];
i+=3;
m_OutCardResultAry.Add(pOutCardResult);
}
}
CardResultAryIndex += AnalyseResult.cbThreeCount;
if (AnalyseResult.cbFourCount>0)
{
//设置结果
BYTE i= 0;
pOutCardResult->cbCardCount=4;
for (BYTE j=0;j<AnalyseResult.cbFourCount;j++)
{
pOutCardResult->cbResultCard[0]=cbCardData[cbCardCount-1-i-CardResultAryIndex];
pOutCardResult->cbResultCard[1]=cbCardData[cbCardCount-1-i-CardResultAryIndex-1];
pOutCardResult->cbResultCard[2]=cbCardData[cbCardCount-1-i-CardResultAryIndex-2];
pOutCardResult->cbResultCard[3]=cbCardData[cbCardCount-1-i-CardResultAryIndex-3];
i+=4;
m_OutCardResultAry.Add(pOutCardResult);
}
}
return true;
}
上面这段代码就犯了这个错误,指针其实就是一个引用,我在第一次操作的时候,我向tagOutCardResultAry添加了一个一项值,tagOutCardResultAry其实就一个记录tagOutCardResult *指针的数组,
然后我又操作第二个值,又添加到该数组里面,第三个,第四个,
后来我到调用的地方这样调用,
tagOutCardResult * pOutCardResult=0;
for(int i=0;i<m_OutCardResultAry.GetCount();i++)
{
pOutCardResult = m_OutCardResultAry.GetAt(i);
DWORD a = pOutCardResult->cbCardCount;
//弹起扑克
for (DWORD j=0;j<a; j++){
BYTE b = pOutCardResult->cbResultCard[j];
CString strcbCardCount;
strcbCardCount.Format("i is %d,j is %d,cbResultCard is %d",i,j,b);
WriteLog(strcbCardCount);
}
}
但是无论i是多少,
2009-05-11 19:53:00 pOutCardResult->cbResultCard[0] is 36
2009-05-11 19:53:00 pOutCardResult->cbResultCard[0] is 54
2009-05-11 19:53:00 pOutCardResult->cbResultCard[0] is 56
2009-05-11 19:53:16 m_OutCardResultAry.GetCount() is 3
2009-05-11 19:53:49 i is 0,j is 0,cbResultCard is 56
2009-05-11 19:54:24 i is 1,j is 0,cbResultCard is 56
2009-05-11 19:54:57 i is 2,j is 0,cbResultCard is 56
2009-05-11 19:53:00 pOutCardResult->cbResultCard[0] is 36
2009-05-11 19:53:00 pOutCardResult->cbResultCard[0] is 54
2009-05-11 19:53:00 pOutCardResult->cbResultCard[0] is 56
这三句是从填充数组的时候的值,说明这中间有三张扑克,
并且都是单张的,形成数组3
这句验证了是正确的
m_OutCardResultAry.GetCount() is 3
2009-05-11 19:53:49 i is 0,j is 0,cbResultCard is 56
2009-05-11 19:54:24 i is 1,j is 0,cbResultCard is 56
2009-05-11 19:54:57 i is 2,j is 0,cbResultCard is 56
这三句表明无论是数组索引为几,
扑克都是56,而不是i=0; 扑克是36,
i=1;扑克是54,
i=3;扑克是56
为什么呢?
我反复的打印日志,思索着,
后来突然醒悟了,
原来指针其实就是一个引用,我的数组中存放的是地址,而不是值,
我在向数组中添加完第一个值后,后来我又改变了指针的值,表面上我向数组中添加了三个值,其实这三个值都是指向的最后一次添加的值,这就是为什么无论i等于多少都只输出相同的值的原因。
指针啊,以前总是听说指针多么难,多么容易犯错,只有这次深入使用了之后才能真正体会到,其实要在c#中我使用引用的时候我内心里就会有一种引用调用就会改变初始值的时候,而在c++中,就没有这种警觉呢?
在使用指针的时候还要注意的就是指针的作用域问题,不能在局部声明的
郁闷!
不过能够真正找到问题所在,还是比较开心的!
最近写了一个小程序,遇到一点小问题,是关于指向字符串的指针的。由此一发不可收拾,好奇心驱使我决定研究一下指针的初始化和赋值规则。略有所得之后,执笔记下,以备后用。
1、指针的初始化
初看起来,指针的初始化和赋值好像很混乱,又是*,又是&,时不时又出来一个数组。其实总结起来很简单:
int *p;
int a=25;
int b[10];
int *m=&a;
int *n=b;
int *r=&b[0];
指针的定义如上所示,以*打头的变量代表该变量为指针变量。
指针初始化时,“=”的右操作数必须为内存中数据的地址,不可以是变量,也不可以直接用整型地址值(但是int *p=0;除外,该语句表示指针为空)。此时,*p只是表示定义的是个指针变量,并没有间接取值的意思。
Int *s=15;
Int *s={2,3,5};
Int *s=a;
以上这三种初始化方式都是错误的。
2、指针的赋值
P=m;
P=&a;
P=b;
*p=25;
*p=a;
*p=b[4];
指针的赋值,“=”的左操作数可以是*p,也可以是p。
当“=”的左操作数是*p时,改变的是p所指向的地址存放的数据;当“=”的左操作数是p时,改变的是p所指向的地址。
数组的变量名b表示该数组的首地址,因此p=b;也是正确的。
3、 “特殊情况”
前面讲到了,指针的初始化必须使用变量地址,而不可以直接使用变量。
那么,下面这个又如何解释呢:
Char *cp=”abcd”;
其实,这个初始化过程,是将指针cp指向字符串的首地址,而并不是传递字符串的值。因为,在C语言里面,没有整体处理一个字符串的机制。
所以,我们的标题“特殊情况”加上了一个引号,因为,它实际上也是以变量地址初始化的指针,“特殊情况”并不特殊。
由此引出,如何使用字符串对指针赋值呢?只有采用下面这种方式:
Cp=”mnop”;
型如*cp=”mnop”;这样的语句是错误的。原因如上所述,字符串常量传递的是它的首地址。
另外,这个初始化过程还有另一层隐含的意思:”abcd”是字符串常量,在初始化过程中并没有发生字符串的复制,而只是简单的将指针指向该字符串常量,因此,不可以通过*cp修改该字符串的值,因为该字符串为常量。当然,我们可以使用“cp=”来修改指针指向的字符串,指针本身并不是常量。
如果试图通过指针*cp来修改该字符串,会出现什么结果,答案是未定义的,要视不同的编译器而定。至少有一点可以确定,在编译阶段,编译器不会报错,因为*cp不是常量,所以对*cp赋值并没有什么语法错误。但是有些编译器,比如VC,会在运行时抛出异常:写入位置 0x00415768(cp指向的地址) 时发生访问冲突!(PS:这也是我的小程序遇到的问题,最后还是在方磊同学的启发下找出了正确答案。这次经历告诉我,不要固守已见,以为自己头脑中的一定是对的。或许翻翻参考书,问题就会迎刃而解!)
这点上与char ca[]=”abcd”;是不同的,通过ca[x]可以修改字符串中的数据。
4、 C语言的经典著作
最后,顺便提一下C语言的经典好书。主要有下面几本:
《The C Programming Language》(C程序设计语言),C语言之父所著,短短二百多页就可以把C语言的各种细节描绘得清清楚楚,不可不谓经典。比起国内四五百页,却不知其所云的C语言程序设计来,真是天壤之别。
《C专家编程》,该书第一版于2002年由人民邮电出版社引进,之后曾一度绝版。最近传出要再版的消息,令人不能不兴奋。同时,这本书也是我近期想要收藏的。
另外,还有两本著作不可不提:《C和指针》、《C陷阱与缺陷》。这两本书也是绝版多年,而且书中很多内容是针对于未标准化前的C提出的。如果能找来电子书的话,作为参考也是不错的。