Monday, August 16, 2010

万恶的指针退化

我估计,还是有人像我一样,懒得不想知道一个指针与数组的区别, 反正用起来都是一样的。比如:
char buff[SIZE];
或者
char *pBuff = new char[SIZE];
除了一个是静态内存,一个是动态,恐怕也再也没有多大分别了。毕竟,用法是大致相仿。如:
sprintf(buff, "hello buff!\n"); // or pBuff
printf("%s\n", buff); // or pBuff, it's OK

至此,一切都还不错。有一天,我们为了安全起见,希望将sprintf改成snprintf,于是:
snprintf(buff, sizeof(buff), "hello buff!\n");
printf("%s\n", buff);
好,完成。嗒嗒嗒,编译运行,通过。一切都还风平浪静。于是,我们继续航行,今天,又写了3000行代码。次日,你发现buff可能行不通,要换成pBuff。所以,你轻易地:
snprintf(pBuff, sizeof(pBuff), "hello buff!\n");
printf("%s\n", buff);
可是,当再次运行时,你发现你的“船”出了问题了。结果,只打印了"hel",不知道是"help",还是"hell"。要知道,sizeof(buff)的结果为SIZE,但sizeof(pBuff)的结果恒为4,即指针的大小。幸运的是,所有的一切祸根来自于你把“buff”改成了“pBuff”,所以你意味到了数组与指针有多么不同。但是,并不是每次都是这么幸运的。我的朋友多次问起我指针与数组的区别。我总是随意地进行了答复,用起来都一样,别理它。终于有一天,我发现了自己犯下了一个低级无耻的错误——别忘了,数组是有大小信息的。
于是,我觉得,我应该将pBuff改回buff比较好:
snprintf(buff, sizeof(buff), "hello buff!\n");
大后天,代码越来越多了,适当的重构变成了一个不错的主意。于是,我将打印放到了一个函数中(当然,真的是算是一个好主意):
void printbuff(char buff[]) {
snprintf(buff, sizeof(buff), "hello buff!\n");
printf("%s\n", buff);
}

int main() {
char buff[SIZE];
printbuff(buff);
...
}

老问题又出现了。buff明明还是数组,为何会错呢?莫非是printbuff的形参中,没有指定大小信息。实际上,数组名在传入函数的时候,数组就退化成了指针,这个退化集中表现为数组的大小信息丢失了(还有其它表现么?)。这就是为什么,C语言的老师在教我们写函数时,除了传递数组名,还要传一个数组大小。
void printbuff(char *buff, int buffSize) {
...
关于数组退化,我记得在 Effective C++ 还是哪本小册子上看过。推荐如我这般的菜鸟阅读!

No comments:

Post a Comment