如果你不理解程序栈如何工作,就很容易犯返回指向局部数据的指针的错误。在下面的例子中,我们重写了3.2.4节中用到的allocateArray
函数。这次我们不为数组动态分配内存,而是用了一个局部数组:
int* allocateArray(int size, int value) {
int arr[size];
for(int i=0; i<size; i++) {
arr[i] = value;
}
return arr;
}
不幸的是,一旦函数返回,返回的数组地址也就无效了,因为函数的栈帧从栈中弹出了。尽管每个数组元素仍然可能包含45,但如果调用另一个函数,就可能覆写这些值。下面的代码段对此做了演示,重复调用printf
函数导致数组损坏:
int* vector = allocateArray(5,45);
for(int i=0; i<5; i++) {
printf("%d\n", vector[i]);
}
图3-6说明了发生这种情况时内存的分配状态。虚线框表示其他的栈帧(比如printf
函数用到的),可能会被推到程序栈上,从而损坏数组持有的内存。栈帧的实际内容取决于实现。
还有一种方法是把arr
变量声明为static
。这样会把变量的作用域限制在函数内部,但是分配在栈帧外面,避免其他函数覆写变量值。
int* allocateArray(int size, int value) {
static int arr[5];
...
}
不过这种方法并不一定总是有用。每次调用allocateArray
函数都会重复利用这个数组。这样相当于每次都把上一次调用的结果覆盖掉。此外,静态数组必须声明为固定长度,这样会限制函数处理变长数组的能力。
如果函数只是返回一个可能的值,而且共享这些值也不会有什么坏处,那么它可以维护一个这些值的列表,然后返回合适的值。如果我们需要返回状态类型的消息,比如不大可能被修改的错误码,这么做就很有用。