以下代码是否合法?
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
答案 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更新。