返回函数中的指针

时间:2009-09-14 08:17:54

标签: c++ c

以下代码是否合法?

char* randomMethod1() {
    char* ret = "hello";
    return ret;
}

和这一个?

char* randomMethod2() {
    char* ret = new char[10];

    for (int i = 0; i < 9; ++i) {
        ret[i] = (char)(65 + i);
    }

    ret[9] = '\0';

    return ret;
}

我会说第一个是合法的,因为你实际上正在做的是返回一个指向字符串文字的指针,我认为它是从程序的字符串表中加载的。但是,我会说第二个不是。我会在第二种方法中说你在堆栈上分配内存,一旦你离开函数,它可能会被另一种方法使用,转向垃圾你要返回的指针。它真的有用吗?

编辑:好的,这是反汇编的代码。任何人都可以解释我怎么能看到它在堆上分配?

char* randomMethod2() {
000536E0  push        ebp  
000536E1  mov         ebp,esp 
000536E3  sub         esp,0E4h 
000536E9  push        ebx  
000536EA  push        esi  
000536EB  push        edi  
000536EC  lea         edi,[ebp-0E4h] 
000536F2  mov         ecx,39h 
000536F7  mov         eax,0CCCCCCCCh 
000536FC  rep stos    dword ptr es:[edi] 
    char* ret = new char[10];
000536FE  push        0Ah  
00053700  call        operator new (511E0h) 
00053705  add         esp,4 
00053708  mov         dword ptr [ebp-0E0h],eax 
0005370E  mov         eax,dword ptr [ebp-0E0h] 
00053714  mov         dword ptr [ret],eax 

    for (int i = 0; i < 9; ++i) {
00053717  mov         dword ptr [i],0 
0005371E  jmp         randomMethod2+49h (53729h) 
00053720  mov         eax,dword ptr [i] 
00053723  add         eax,1 
00053726  mov         dword ptr [i],eax 
00053729  cmp         dword ptr [i],9 
0005372D  jge         randomMethod2+5Fh (5373Fh) 
        ret[i] = (char)(65 + i);
0005372F  mov         eax,dword ptr [i] 
00053732  add         eax,41h 
00053735  mov         ecx,dword ptr [ret] 
00053738  add         ecx,dword ptr [i] 
0005373B  mov         byte ptr [ecx],al 
    }
0005373D  jmp         randomMethod2+40h (53720h) 

    ret[9] = '\0';
0005373F  mov         eax,dword ptr [ret] 
00053742  mov         byte ptr [eax+9],0 

    return ret;
00053746  mov         eax,dword ptr [ret] 
}
00053749  pop         edi  
0005374A  pop         esi  
0005374B  pop         ebx  
0005374C  add         esp,0E4h 
00053752  cmp         ebp,esp 
00053754  call        @ILT+320(__RTC_CheckEsp) (51145h) 
00053759  mov         esp,ebp 
0005375B  pop         ebp  
0005375C  ret              

6 个答案:

答案 0 :(得分:17)

两者都是合法的。在第二个中,您从堆栈中分配内存。您正在使用new并从堆中分配内存。如果你没有使用delete释放第二种方法返回的指针,你将会有内存泄漏

顺便说一句,堆栈分配的数组声明如下:

char x[10]; // note that there's no `new`.

更新

这一行调用operator new,它从堆中分配内存并初始化对象。

00053700  call        operator new (511E0h) 

答案 1 :(得分:8)

事实上两者都是合法的。在第二种情况下,您在堆中分配内存,而不是在堆栈上。这一行:

00053700  call        operator new (511E0h)

是对operator new的调用,负责内存分配。

堆栈上的分配如下:

char* randomMethod2() {
    char ret[10];
    ....
    return ret;
}

确实会导致未定义的行为。

但是不要忘记,在第一种情况下,尝试通过返回的指针修改内存也会导致未定义的行为。在第二种情况下,调用者负责释放内存(调用delete[])。

答案 2 :(得分:3)

我认为两者都是合法的! new分配的内存有效,直到我们明确调用delete或程序本身死掉!

答案 3 :(得分:3)

使用相当现代的编译器首先不应该编译(或者至少发出警告)固定版本将是:

const char* randomMethod1() {
    const char* ret = "hello";
    return ret;
}

因为“hello”是一个常量(在静态初始化空间中),所以它的地址可以正常使用(但是只读)。第二个例子是完全合法的:你可以传递(并使用!)一个用new分配的指针,直到调用delete为止。在这种情况下,您认为必须使用delete [](在分配数组时使用括号运算符)

答案 4 :(得分:0)

两者都在堆上分配变量,所以当你从函数返回控制时,两个指针都将处于活动状态。

在第二个功能

00053700呼叫运营商新(511E0h)

调用new来分配内存空间。

如果你在返回控件之前使用delete [] ret释放ret,那么第二个将是非法的;

要在堆上分配变量,您必须编写如下:int ret [10]; 现在你不能返回指针ret因为它会在函数结束时被销毁。

答案 5 :(得分:-2)

两者都是合法的。但是,在我看来,第一个可能导致相当多的头部刮伤。从语义上讲,它与第二个完全不同。

第一个

它是一个字符串文字,它将在函数外部存活,并且在程序的整个持续时间内有效,因此它在堆上声明。

我对第一种方法的初步反应是错误的。我原以为它是在堆栈上声明的,因为它是一个局部变量,但这不准确。因为它是一个字符串文字,所以它的值存储在堆上。但是,您创建并从函数返回的指针位于堆栈中。

第二个

第二种是通过new在堆上声明内存,但是你必须确保稍后使用delete显式释放它以避免内存泄漏。调用free后,内存无效。

是的,每次调用new都会在堆上分配内存,而不是堆栈。

修改:根据Pete在下方的评论及this question更新。