《挑战编程》实在是一本很有意思的书。上面的ACM很有特点。但可惜的是没有很好的答案。网上貌似也找不到有相关的信息。
下面这道题来自《挑战编程》1.6.5 之Graphical Editor。是写一个命令解释器。
先是头文件:
#define ROW 10
#define COL 10
#define CODELEN 12
#define PRA1 0
#define PRA2 2
#define PRA3 4
#define PRA4 6
#define PRA5 8
#define PRA6 10
char Pix[ROW][COL];
int M,N;
void ClrPix(char []);
void OutputPix(char []);
void ColourXY(char []);
void ColourRow(char []);
void ColourCOL(char []);
void ColourRect(char []);
void ColourR_Pre(char []);
void ColourR(char []);
void EmptyFunc(char []);
void ExitPro(char []);
void InitFuncArray(void);
static void (*pfunc[26])(char []); //要用static声明
以下是代码:
/*START:挑战编程P15-1.6.5*/
//用转移表代替switch实现不同的选择
void
main(void)
{
char code[CODELEN];
InitFuncArray(); //初始化函数指针数组
while(gets(code))
(*pfunc[code[PRA1]-'A']) (code);
Error("Need for the code to quit the program...");
}
/*END*/
/*START:初始化函数指针数组*/
void
InitFuncArray(void)
{
int i;
for(i=0;i<26;i++)
pfunc[i]=EmptyFunc;
pfunc['I'-'A']=ClrPix;
pfunc['C'-'A']=ClrPix;
pfunc['L'-'A']=ColourXY;
pfunc['H'-'A']=ColourRow; //画一条水平线
pfunc['V'-'A']=ColourCOL; //画一条竖直线
pfunc['K'-'A']=ColourRect; //画矩形
pfunc['F'-'A']=ColourR_Pre; //填充R区域之预处理
pfunc['S'-'A']=OutputPix; //输出图像
pfunc['X'-'A']=ExitPro;
}
/*END*/
/*START:初始化像素区*/
void
ClrPix(char code[])
{
int i,j;
if(code[PRA1]=='I'){
M=code[PRA3]-'0'; N=code[PRA2]-'0'; //M为行,N为列
}//if
for(i=1;i<=M;i++){
for(j=1;j<=N;j++)
Pix[i][j]='0';
}//for
}
/*END*/
/*START:给点X,Y上色*/
void
ColourXY(char code[])
{
Pix[code[PRA3]-'0'][code[PRA2]-'0']=code[PRA4];
}
/*END*/
/*START:画一条水平线*/
void
ColourRow(char code[])
{
int j;
for(j=code[PRA2]-'0';j<=code[PRA3]-'0';j++)
Pix[code[PRA4]-'0'][j]=code[PRA5];
}
/*END*/
/*START:画一条竖直线*/
void
ColourCOL(char code[])
{
int i;
for(i=code[PRA3]-'0';i<=code[PRA4]-'0';i++)
Pix[i][code[PRA2]-'0']=code[PRA5];
}
/*END*/
/*START:画一个实心矩形*/
void
ColourRect(char code[])
{
code[PRA2]=code[PRA3];
code[PRA3]=code[PRA5];
code[PRA5]=code[PRA6];
for(code[PRA5]=code[PRA2];
code[PRA5]<=code[PRA4];code[PRA5]++)
ColourRow(code);
}
/*END*/
/*START:填充区域R-预处理*/
void
ColourR_Pre(char code[])
{
code[PRA1]=Pix[code[PRA3]-'0'][code[PRA2]-'0']; //code的第一个参数成为标准颜色
ColourR(code);
}
/*END*/
/*START:填充区域R*/
void
ColourR(char code[])
{
int i,j;
i=code[PRA3]-'0'; j=code[PRA2]-'0';
if((i>=1&&i<=M)
&&(j>=1&&j<=N)
&&Pix[i][j]==code[PRA1])
{
ColourXY(code);
code[PRA2]--;
ColourR(code);
code[PRA2]++;
code[PRA2]++;
ColourR(code);
code[PRA2]--;
code[PRA3]++;
ColourR(code);
code[PRA3]--;
code[PRA3]--;
ColourR(code);
code[PRA3]++;
}//if
}
/*END*/
/*START:输出图像*/
void
OutputPix(char code[])
{
int m,n;
printf("%s/n",&code[PRA2]);
for(m=1;m<=M;m++){
for(n=1;n<=N;n++)
printf("%c",Pix[m][n]);
puts("");
}
}
/*END*/
/*START:空函数*/
//仅当当遇到无效指令时执行
void
EmptyFunc(char code[])
{
return;
}
/*END*/
/*START:退出程序*/
//当且仅当指令为X时调用
void
ExitPro(char code[])
{
exit(EXIT_SUCCESS);
}
/*END*/
这是完全OK的代码。
现在写程序的水平一般是:编译没问题,很显然的bug不会有。但在运行一定出问题,要么运行的不正确,要不就是程序崩溃。
写完这程序后,果然不出所料的程序崩溃了……
问题是,在程序调用中,InitFuncArray完全没问题,且每个函数的函数名(实际上是一个指针)没问题。但在main中的第15行的while只做了一次,只是第一条命令执行了,然后程序就崩溃了……我惊奇了半天,依次检查,貌似都没有问题。
后来,才发现,是我定义的全局数组void (*pfunc[26])(char []); 中的函数指针值发生了改变……(补充一句,一开始,我定义的只是void (*pfunc[26])(char []); ,并没有像现在的头文件中的28行所定义的那样,使用static来修饰)也就是说,pfunc数组中保存的函数名地址发生了改变……例如函数ColourXY,在InitFuncArray中初始化时是……3033。此时在pfunc中保存的也是该值。但在main中while调用到该函数时,再取到数组pfunc中保存的该函数的地址时,却发生了变化,不再是原来的……3033,而变成了 另一个完全不同的值。加上static后就OK了……
我就很是想不通,pfunc是被定义为了全局数组的,里面的值在程序退出之前是不会改变,但这里为什么改变了?……记得原来看《C专家编程》时,里面有提到,当在子函数中开辟指针指向的动态数组并返回该指针时,要将其声明为static。于是为了避免麻烦,我在这里就将之声明为全局数组,想不到还是出错。这是神奇的问题……
如果你知道为什么,麻烦告诉我一声。
代码看起来很多,但却是很简单的。
还有ColourR中,我一直想把他写得更简洁一些,奈何硬是写不好……太寒了……
*头文件中的Fatal.h是我自己写的宏。