今天和同学一起调试绘制灰度直方图的一段程序,如下所示:
int m_nRHisto[255];int m_nGHisto[255];int m_nBHisto[255];
unsigned char R,G,B;
int RR,GG,BB;
static int nDx[] = {-1,0,1,0,1,1,-1,-1};static int nDy[] = {0,1,0,-1,1,-1,1,-1};
int k,x,y;int temp;
for ( i = 0; i < 256; i++ )
{
m_nRHisto[i] = 0;m_nGHisto[i] = 0;m_nBHisto[i] = 0;
}
long lLineBytes = WIDTHBYTES( lWidth * 24 );
for ( i = 0; i < lHeight; i++ )
{
for ( j = 0; j < lWidth; j++ )
{
R = lpDIBbits[ (lHeight-1-i) * lLineBytes + j*3 ];
G = lpDIBbits[ (lHeight-1-i) * lLineBytes + j*3 + 1 ];
B = lpDIBbits[ (lHeight-1-i) * lLineBytes + j*3 + 2 ];
for ( k = 0; k < 8; k++ )
{
x = j+nDx[k];y = i+nDy[k];
if ((x < lWidth) && (x>=0) && (y<lHeight) && (y>=0))
{
RR = lpDIBbits[(lHeight-1-y)*lLineBytes+x*3];temp = abs(R-RR);
m_nRHisto[temp]++;
GG = lpDIBbits[(lHeight-1-y)*lLineBytes+x*3+1];temp = abs(G-GG);
m_nGHisto[temp]++;
BB = lpDIBbits[(lHeight-1-y)*lLineBytes+x*3+2];temp = abs(B-BB);
m_nBHisto[temp]++;
}
}
}
}
该同学是将灰度直方图绘制在一个对话框中,但是点击对话框中的OK按钮时,不仅对话框消失,主程序也随之退出。于是调试之,初步怀疑是这段统计RGB分量的地方有些问题,最后发现是数据类型设置有误造成数组溢出,呵呵,小错误大失误。你看出是那里有问题?没错:
int RR,GG,BB;
就是这里数据类型设置错误。程序的本意R和RR都是存储影像的灰度,但是R是unsigned char类型的,RR是int类型,前者设置成unsigned char型完全没有问题,后者设置成int型好像也说得过去,但是如果两者相减,会出现什么后果,可是大有问题的。那下面我将逐步调试,将这个错误分析一下。
(1) 将断点设置在“R=”这句话处,调试
赋值式右边是个负值(这是因为lpstr类型是指向字符串的),而左值R的类型是unsigned char类型,那么这里会出现一个溢出的情况,尽管这个溢出可能引起什么错误,但从逻辑上讲还是有些问题的。unsigned char类型的范围是0~255(8位),那么如果将一个负值赋给它,则会首先将该负值对256取模,即-108+256=148.即此时R为148,安全的值,此时不会有什么错误,如下:
(2) 继续调试,在“RR=”处停住,调试
此时右值仍然是负值,即:
int即signed int,因为int、short和long类型默认为带符号型,所以当右值为负值,将其赋给左值,那么RR也是负值,如上所示。负值就负值吧,又没招惹谁,程序也没有什么错误。
(3) 继续调试,要命的来了,在“temp = abs(R-RR)”处停住,看看temp的结果是什么吧:
oops,对于表达式R-RR,R为unsigned char,RR为int,那么在运算的时候,R会自动提升为int型,此时R=148,RR保持不变,RR=-112,两者相减,temp=260。260就260吧,反正temp是int型够大,好像还是没有错误,别急,下一句就真的要命了。
(4) F10继续,涉及到数组了,小心:
m_nRHisto[temp]++;
回头看看m_nRHisto的定义: int m_nRHisto[255],那么temp=260,数组明显溢出,这是最根本的错误。但是对于这种溢出,编译器一般是不会报错的,所以要特别小心。
找出错误,那么更改就很方便了,将RR/GG/BB的类型改得和R/G/B一致就可以了,并不需要都是unsigned char,但是从节约角度来讲,用unsigned char来表达灰度是非常合适的。
同时,从逻辑的完备性考虑在给temp赋值之后,可以加一个判断语句:
if( temp > 255 ) return;
找出这个错误,我又回头翻翻几天前看的C++PRIMER4th,在p31上给出的一段建议:
“同样的道理,虽然char类型是整型,但是char类型通常用来存储字符而不用于计算。事实上,在某些实现中char类型被当作signed类型,在另外一些实现中则被当作unsigned类型,因此把char类型作为计算类型使用时容易出问题。
在大多数机器上,使用int类型进行整型计算不易出错。就技术而言,int类型用16位表示—这对大多数应用来说太小了。实际应用中,大多数通用机器都是使用和long类型一样长的32位来表示int类型”
更重要的是,C++PRIMER4th整本书都在强调这样一个信息:
“与使用标准vector类型的程序相比,依赖于内置数组的程序更容易出错而且难于调试”。
该书可以说一直都在介绍容器类型,大有让C++程序员摆脱数组和指针,转而用容器和迭代器的想法。下面一段代码可以简单说明这一思想:
const size_t array_size = 10;
int ia[ array_size ];
for( size_t ix = 0; ix != array_size; ++ix )
ia[ix] = ix;
容器和迭代器也在持续学习中,希望能在今后的实践中用到。