函数返回局部变量

看看下面的程序的输出:

#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 中都引用了,但在内存中却只有一份拷贝,这与静态变量性质相当神似。

v

ly:CR �.r;��f,��f,ii-theme-font:minor-latin;mso-fareast-font-family:宋体;mso-fareast-theme-font:minor-fareast;mso-hansi-font-family:Calibri;mso-hansi-theme-font:minor-latin'>重载的operator new必须含有一个size_t参数,表示要分配内存的对象的长度,同时必须返回一个指向等于这个长度的对象的指针。若没有找到符合要求的存储单元,构造函数将不被调用,另外需要一个表示失败的返回值以外还应该抛出异常(可以自定义)。

Operator delete参数为一个指向由operatornew分配内存的void*(已调用析构函数后得到的指针),其返回类型为void。

演示示例

3.3.2重载类中的new/delete

重载类中的new实际上创建的是一个static成员函数,该new只为创建该类对象起作用,不会影响默认的全局版本new,但是需要注意名字隐藏的问题。

注意:任何类中只要提供了自己的operator new或operator new[],那么就得同时提供对应的类相关版本的plain new,placement new以及nothrow new,否则根据名字隐藏的规则(将全局new遮掩掉了),将会发生没有可用匹配的错误。

一旦为类重载了operator new与operator delete,那么无论何时创建这个类的对象,都将调用这些重载的运算符,但若创建该类的一个对象数组时,全局operator new将立即被调用,用来为这个数组分配足够的内存,因此要避免出现这种情况,需要重载operator new的数组版本:operator new[]与operator delete[]。

演示示例:(item 36 in exceptional c++)

1.             //问题1:为什么B的delete有第二个参数而D没有?2.             class B 3.             {4.             public:5.               virtual ~B();6.               void operator delete ( void*,size_t ) throw();7.               voidoperator delete[]( void*, size_t ) throw();8.               void f( void*, size_t )throw();9.             };10.          class D : public B11.          {12.         public:13.            void operator delete ( void* )throw();14.            void operatordelete[]( void* ) throw();15.          };16.          void f()17.          {18.            //问题2:下面各个语句中,调用的是哪一个delete以及调用时的参数,为什么?19.       D* pd1 = new D; 20.       delete pd1;21.       B* pb1 = new D;22.       delete pb1;23.       D* pd2 = new D[10];24.       delete[] pd2;25.       B* pb2 = new D[10];26.       delete[] pb2;27.     28.      //问题3:下面两个赋值语句合法吗?29.       typedef void(B::*PMF)(void*, size_t); 30.       PMFp1 = &B::f;31.       PMF p2 =&B::operator delete;32.     }3.4 内存分配失败问题

A 内存分配失败的报告方式:大多数new通过抛出bad_alloc异常来报告分配失败;nothrownew则通过C中malloc方式报告失败,即仅返回空指针,永远不会抛出异常。

B 内存分配失败的处理过程:通过set_new_handler调用错误处理函数new_handler,检查指向函数的指针,若指针非0,则指向的函数被调用。

new_handler与set_new_handler的原型如下:

typedef void (*new_handler)();

new_handler set_new_handler(new_handler p)throw();

new_handler是一个typedef,表现出一个函数指针,该函数没有参数也没有传回值,而set_new_handler是一个函数,需要一个new_handler参数并传回一个new_handler。

Set_new_handler的参数指针指向的函数正是当operatornew无法配置足够内存时,应该去调用的函数,其传回值是一个函数指针,指向先前登陆过的new_handler。用法示例如下:

void nomorememory()

{

      cerr << "unable to satisfy request for memory\n";

      abort();

}

int main()

{

      set_new_handler(nomorememory);

      int *pbigdataarray = new int[100000000];

      ...

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值