C语言编程不易发现的错误(二)
错误:
c语言函数返回局部变量地址。
分析:
c语言函数是不能返回局部变量地址(特指存放于栈区的局部变量地址),除非是局部静态变量地址,字符串或整型常量地址、动态分配地址。其原因是一般局部变量的作用域只在函数内,其存储位置在栈区中,当程序调用完函数后,局部变量会随此函数一起被释放。其地址指向的内容不明(原先的数值可能不变,也可能改变)。而局部静态变量地址,字符串或整型常量地址都是存放在数据区、动态分配地址存放在堆区,函数运行结束后只会释放栈区的内容。
实例及结果:
#include "stdio.h"
//返回局部变量a的地址
int* f1()
{
int a = 9;
int *pa = &a;
printf("a = %d\t&a = %p \n",a, &a);
printf("*pa = %d\tpa = %p \n",*pa, pa);
return pa;
}
int main()
{
int b = 5;
int *pb = &b;
printf("before: b = %d\t&b = %p\n",b, &b);
printf("before: *pb = %d\tpb = %p\n",*pb, pb);
pb = f1();
//printf("after : b = %d\t&b = %p\n",b, &b);
printf("after : *pb = %d\tpb = %p\n",*pb, pb);
return 1;
}
#include "stdio.h"
//返回局部变量a的地址
int* f1()
{
int a = 9;
int *pa = &a;
printf("a = %d\t&a = %p \n",a, &a);
printf("*pa = %d\tpa = %p \n",*pa, pa);
return pa;
}
int main()
{
int b = 5;
int *pb = &b;
printf("before: b = %d\t&b = %p\n",b, &b);
printf("before: *pb = %d\tpb = %p\n",*pb, pb);
pb = f1();
printf("after : b = %d\t&b = %p\n",b, &b);
printf("after : *pb = %d\tpb = %p\n",*pb, pb);
return 1;
}
总结1
如上2个实例便是在函数中返回局部变量地址。其中第1个例子直接输出*pb,其结果是对的。但若像第2个例子一样,在输出前添加如下语句:
printf(“after : b = %d\t&b = %p\n”,b, &b);
其结果是错误的,因为此语句修改栈区内容。所以返回局部变量地址,其结果可能正确,也可能错误,这取决于函数之后的代码是否使用并修改栈区内容。而计算机编程必须确保程序执行正确性和唯一性,任何不确定的运行结果都是错误的。
#include "stdio.h"
//返回静态局部变量a的地址
int* f1()
{
static int a = 9;
int *pa = &a;
printf("a = %d\t&a = %p \n",a, &a);
printf("*pa = %d\tpa = %p \n",*pa, pa);
return pa;
}
int main()
{
int b = 5;
int *pb = &b;
printf("before: b = %d\t&b = %p\n",b, &b);
printf("before: *pb = %d\tpb = %p\n",*pb, pb);
pb = f1();
printf("after : b = %d\t&b = %p\n",b, &b);
printf("after : *pb = %d\tpb = %p\n",*pb, pb);
return 1;
}
#include "stdio.h"
//返回字符串常量的地址
char* f1()
{
int a = 9;
char *pa = "a";
printf("a = %d\t&a = %p \n",a, &a);
printf("*pa = %d\tpa = %p \n",*pa, pa);
return pa;
}
int main()
{
int b = 5;
char *pb = "b";
printf("before: b = %d\t&b = %p\n",b, &b);
printf("before: *pb = %d\tpb = %p\n",*pb, pb);
pb = f1();
printf("before: b = %d\t&b = %p\n",b, &b);
printf("after : *pb = %d\tpb = %p\n",*pb, pb);
return 1;
}
#include "stdio.h"
//返回整型常量的地址
int* f1()
{
int a[3] = {9,6,3};
int *pa = a;
printf("a = %d\t&a = %p \n",a[0], a);
printf("*pa = %d\tpa = %p \n",*pa, pa);
return pa;
}
int main()
{
int b = 5;
int *pb = &b;
printf("before: b = %d\t&b = %p\n",b, &b);
printf("before: *pb = %d\tpb = %p\n",*pb, pb);
pb = f1();
printf("before: b = %d\t&b = %p\n",b, &b);
printf("after : *pb = %d\tpb = %p\n",*pb, pb);
return 1;
}
#include "stdio.h"
#include "stdlib.h"
//返回动态分配的地址
int* f1()
{
int a = 9;
int *pa = (int*) malloc(8);
*pa = a;
printf("a = %d\t&a = %p \n",a, &a);
printf("*pa = %d\tpa = %p \n",*pa, pa);
return pa;
}
int main()
{
int b = 5;
int *pb = &b;
printf("before: b = %d\t&b = %p\n",b, &b);
printf("before: *pb = %d\tpb = %p\n",*pb, pb);
pb = f1();
printf("before: b = %d\t&b = %p\n",b, &b);
printf("after : *pb = %d\tpb = %p\n",*pb, pb);
return 1;
}
总结2
从以上4个例子可看出,返回静态局部变量的地址、常量的地址和动态分配的地址,其结果都是正确的。以上地址指向的内容都不在栈区中,不受释放函数的影响。
虽然以上3种方式是正确的,但如果不是一定要这样处理,个人不建议在函数中返回任何局部变量的地址。