返回函数局部变量的问题

来源:互联网 发布:网店装修软件 编辑:程序博客网 时间:2024/05/17 07:47
#include <stdio.h>
char *returnStr()

{
     char *p="hello world!";
     return p;
}
int main()
{
     char *str;
     str=returnStr();
     printf("%s\n", str);
     return 0;
}

这个没有任何问题,因为"hello world!"是一个字符串常量,存放在静态数据区,把该字符串常量存放的静态数据区的首地址赋值给了指针,所以returnStr函数退出时,该该字符串常量所在内存不会被回收,故能够通过指针顺利无误的访问。

但是,下面的就有问题:

#include <stdio.h>
char *returnStr()
{
     char p[]="hello world!";
     return p;
}
int main()
{
     char *str;
     str=returnStr();
     printf("%s\n", str);
     return 0;
}

"hello world!"是一个字符串常量,存放在静态数据区,没错,但是把一个字符串常量赋值给了一个局部变量(char []型数组),该局部变量存放在栈中,这样就有两块内容一样的内存,这是与前着最本质的区别,当returnStr函数退出时,栈要清空,局部变量的内存也被清空了,所以这时的函数返回的是一个已被释放的内存地址,所以打印出来的是乱码。

如果函数的返回值非要是一个局部变量的地址,那么该局部变量一定要申明为static类型。如下:

#include <stdio.h>
char *returnStr()
{
     static char p[]="hello world!";
     return p;
}
int main()
{
     char *str;
      str=returnStr();
     printf("%s\n", str);

     return 0;
}

这个问题可以通过下面的一个例子来更好的说明:

#include <stdio.h>
//返回的是局部变量的地址,该地址位于动态数据区,栈里
char *s1()
{
     char p[]="Hello world!";
     printf("in s1 p=%p\n", p);
     printf("in s1: string's address: %p\n", &("Hello world!"));
     return p;
}
//返回的是字符串常量的地址,该地址位于静态数据区
char *s2()
{
     char *q="Hello world!";
     printf("in s2 q=%p\n", q);
     printf("in s2: string's address: %p\n", &("Hello world!"));
     return q;
}
//返回的是静态局部变量的地址,该地址位于静态数据区
char *s3()
{
     static char r[]="Hello world!";
     printf("in s3 r=%p\n", r);
     printf("in s3: string's address: %p\n", &("Hello world!"));
     return r;
}
int main()
{
     char *t1, *t2, *t3;
     t1=s1();
     t2=s2();
     t3=s3();
     printf("in main:");
     printf("p=%p, q=%p, r=%p\n", t1, t2, t3);
     printf("%s\n", t1);
     printf("%s\n", t2);
     printf("%s\n", t3);
     return 0;
}

运行输出结果:
in s1 p=0xbff92efb
in s1: string's address: 0x80486ac
in s2 q=0x80486ac
in s2: string's address: 0x80486ac
in s3 r=0x804998c
in s3: string

这个结果正好应证了上面解释,同时,还可是得出一个结论:字符串常量,之所以称之为常量,因为它可一看作是一个没有命名的字符串且为常量,存放在静态数据区。这里说的静态数据区,是相对于堆、栈等动态数据区而言的。静态数据区存放的是全局变量和静态变量,从这一点上来说,字符串常量又可以称之为一个无名的静态变量,因为"Hello world!"这个字符串在函数 s1和s2 中都引用了,但在内存中却只有一份拷贝,这与静态变量性质相当神似。

char *p = "abcdefg";//静态存储区

char p[] = "abcdefg"; // p本身是数组名了,数组里放的字符串,是局部变量,内容是原来的静态区域内容的拷贝!因此返回p实际上返回的局部变量地址而不是静态存储区地址,和上面不同!

函数返回指针。本来就是一个比较容易出问题的操作。在霍顿的《VC++ 入门经典》一书中,给出了一个很有代表性的例子,如下:

  // Ex5_11.cpp

  #include <iostream>

  using std::cout;

  using std::endl;

  double* treble(double); // Function prototype

  int main(void)

  {

  double num = 5.0; // Test value

  double* ptr = 0; // Pointer to returned value

  ptr = treble(num);

  cout << endl

  << "Three times num = " << 3.0*num;

  cout << endl

  << "Result = " << *ptr; // Display 3*num

  cout << endl;

  system("pause");

  return 0;

  }

  // Function to treble a value - mark 1

  double* treble(double data)

  {

  double result = 0.0;

  result = 3.0*data;

  return &result;

  }

  两个输出语句,一个直接输出3*5=15.另一个在一个函数中进行了乘法运算,也是5*3, 存到result变量中也没有任何问题。返回这个变量的指针,输出时再接触引用。貌似也没有错误。两条输出语句似乎都应该输出15.但事实不是这样。编译 器会抛出[Warning] address of local variable `result' returned 这样一个警告信息。程序运行后的结果也并非是我们预想的那样。第二条输出语句会输出一个不可预见的值。这是怎么回事呢?

  仔细分析一下,result是作用域在treble函数中的局部变量。当函数执行结束后。变量result会被析构。其原先占用的内存区域已经被系统回收,可以存储任何数据。而返回的指向该地址的指针也失去了其原有的意义。因此我们得到这样一条准则:

  永远不要从函数中返回局部自动变量的地址。

  如果你真的需要这样操作。你可以在函数的参数表中传入一个指针变量。然后将需要写入的数据写入到该指针变量指向的地址。由于该指针指向的变量,作用域在函数体 之外。因此不会在函数结束结束时被回收。

  现在回到我们遇到的问题。时间函数localtime就是一个返回指针的函数。返回值类 型:tm*

  该如何接收这个返回值?当然是声明一个与之类型相同的变量。

  于是你会这样写:tm* result;

  接下来呢?还用问?赋值嘛。是不是这样:result=localtime(....);

  返回什么类型,当然要给什么类型的变量 赋值。但是这样却发生了我们不想看到的结果。

  也许你想到了。就是这个指针!返回的指针在函数结束后不再有效。正确的方法可以是:

  tm result;

  result = *localtime(....);

  也可以是:

  tm* result;

  *result = *localtime(...);

  正确的方法的共同特点是在函数结束前,对返回的指针解除引用。然后用这个数值,为变量或指针指向的内存区域赋值。也就是说必须要复制函 数的返回值。因为函数体中变量会被析构。