(被调)函数内的局部变量在函数返回时被释放,不应被外部引用。虽然并非真正的释放,通过内存地址仍可能访问该栈区变量,但其安全性不被保证。后续若还有其他函数调用,则其局部变量可能覆盖该栈区内容。常见情况有两种:前次调用影响当前调用的局部变量取值(函数的"遗产");被调函数返回指向栈内存的指针,主调函数通过该指针访问被调函数已释放的栈区内容(召唤亡灵)。
1 函数的"遗产"
【示例1】先后连续调用Ancestor和Sibling函数,注意函数内的dwLegacy整型变量。
1 void Ancestor(void){ 2 int dwLegacy = 42; 3 } 4 void Sibling(void){ 5 int dwLegacy; 6 printf("%d ", dwLegacy); 7 } 8 int main(void){ 9 Ancestor(); 10 Sibling(); 11 return 0; 12 }
若使用普通编译(如gcc test.c),则输出42,因为编译器重用之前函数的调用栈;若打开优化开关(如gcc -O test.c),则输出一个随机的垃圾数,因为Ancestor函数将被优化为空函数,也不会被main函数调用。
因此,为避免这种干扰,建议声明自动局部变量时对其显式赋初值(初始化)。
【示例2】先后调用Ancestor和Sibling函数,注意函数内的aLegacy数组变量。
1 void Ancestor(void){ 2 int aLegacy[10], dwIdx = 0; 3 for(dwIdx = 0; dwIdx < 10; dwIdx++) 4 aLegacy[dwIdx] = dwIdx; 5 } 6 void Sibling(void){ 7 int aLegacy[10], dwIdx = 0; 8 for(dwIdx = 0; dwIdx < 10; dwIdx++) 9 printf("%d ", aLegacy[dwIdx]); 10 }
若使用普通编译,则输出0 1 2 3 4 5 6 7 8 9(Ancestor函数内的数组赋值会影响Sibling函数的数组初值);若打开优化开关,则输出一串随机的垃圾数。
【示例3】连续调用两次Func函数。
1 void Func(void){ 2 char acArr[25]; 3 printf("%s ", acArr); //注意此句打印结果 4 acArr[0]= 'a'; acArr[1] = 'b'; acArr[2] = 'c'; acArr[3]= '