一段神奇的 C++代码,大家觉得有没有问题?

2019-04-26 16:15:01 +08:00
 Wangjl

如下代码,我在 vs2015 中,for 循环 1000 遍,没有问题,10000 遍就报错

大家觉得是哪里的问题呢?

#include <stdio.h>

void f(char* p)
{
	delete[] p;
	p = new char[20000];
}

int main()
{
	
	for (int i = 0; i < 10000; i++)
	{
		char *p = new char[2];
		f(p);
		delete[] p;
	}
	getchar();
	return 0;
}

按理说以上代码只是在循环创建 20000 个字节的堆内存,创建了又销毁,不应该出问题才对啊。

大家有什么看法?

7263 次点击
所在节点    C
55 条回复
binlaten
2019-04-26 17:30:31 +08:00
@wutiantong 内存分页机制,一般 4k,4k 以内,有较大概率分到相同地址,大于 4k 这个概率就小很多了
AngryMagikarp
2019-04-26 17:36:58 +08:00
f()里的 p 只是一个临时变量。要实现你预想的结果,需要用双重指针。如下:

void f(char** p){
delete[] *p;
*p = new char[20000];
}

int main(){
for (int i = 0; i < 10000; i++)
{
char *p = new char[2];
f(&p);
delete[] p;
}
return 0;
}
nonkr
2019-04-26 17:42:31 +08:00
你需要传入两重指针,单重指针是不能这么操作的
Wangjl
2019-04-26 18:27:06 +08:00
@nonkr
@AngryMagikarp
@wutiantong
@bb123
一语道破玄机, 就是这样的。 因为指针传递,相当于值传递,进去的 p 已经不是原来的 p 指针了,而是局部变量的 p,
当在函数中释放一次后,新申请的空间实际上是给了局部变量的 p,而局部变量随着函数的销毁而销毁,因此在外部
再次 delete 的时候,相当于进行了二次 free,所以会出问题。v 站大神真多啊,学到了。
Wangjl
2019-04-26 18:55:34 +08:00
解决方法就是用双重指针或者引用
stephenyin
2019-04-26 19:17:31 +08:00
这是一款出镜率极高的 C/C++ 基础面试题.
dabaibai
2019-04-26 19:48:18 +08:00
C 代码 这不是 C++
dabaibai
2019-04-26 19:48:41 +08:00
@dabaibai 当我没说,我看到 new delete.
iwong0exv2
2019-04-26 20:21:44 +08:00
去面华为吧!
当年我去面的时候给了道环链表检测的题,要求实现 bool test(const LIST_ENTRY *p),p 是链表头。我上来就是 p=p->next;。面试官说我这样直接改指针,会影响外面的链表。我说这种编码风格可能不太规范,但真不会修改到外面的链表。他说这传的是指针啊,你知道指针的用法吗?
后面当然没通过。
GeruzoniAnsasu
2019-04-26 20:34:03 +08:00
我在想 new char[2]的那个 p 被 delete 了两次为啥不会崩,1000 次 都没崩
smdbh
2019-04-26 20:39:55 +08:00
基础了吧,不应该了
huaouo
2019-04-26 20:46:59 +08:00
@wutiantong 那个大概叫 空悬指针?
yippees
2019-04-26 22:10:19 +08:00
1、指针的指针

2、谁申请谁释放原则

3、返回指针
char* f(char* p)
{
if(p!=NULL)
delete[] p;
p = new char[20000];
return p;
}

int main()
{

for (int i = 0; i < 100000; i++)
{
char *p = new char[2];
p=f(p);
if (p != NULL)
delete[] p;
}
printf("AAA");
getchar();
return 0;
}


//和神奇无关
hihibin
2019-04-26 23:11:34 +08:00
@Wangjl 如果是 double free,你 1000 次没问题,一万次就有问题了,说明应该不是。
我觉得就像楼上的,需要判断是否为 NULL,如果 char *p = new char[2]; 时,分配不到内存,p=NULL,再传入 f (),
p = new char[20000];这样就对 0 地址直接操作了,会报错吧。
radiolover
2019-04-26 23:16:59 +08:00
国内互联网越来越水是有原因的
jackmod
2019-04-26 23:21:35 +08:00
不要说 1000 次,1 次都不对。

main()里的 p 分配在 main()的栈上,f()里的 p 分配在 f()的栈上。
main()的栈和 f()的栈是两回事,所以那两个 p 就是 2 个东西。

解释一下楼上的某个建议:f()改为 f(char**),并传入&p。
&p 是 main()上的栈的某个位置,传给 f()后,f()的 stack 上会分配一个 char**(假设为 pp ),它指向(*pp )的位置才是 main()上的栈的某个位置,也就是 main()里的 p。
xiaottt
2019-04-26 23:27:34 +08:00
骗铜币的吧。。。233333333
ipwx
2019-04-26 23:29:07 +08:00
@binlaten 你忘了 libc 的内存分配算法还有一个小内存块回收再利用的策略。
lynskylate
2019-04-26 23:39:40 +08:00
....就不该这么写,同一作用域分配的内存尽量在同一作用域内 shifang
yuikns
2019-04-27 00:07:35 +08:00
@GeruzoniAnsasu 因为是 VS。

clang 一次直接 Abort trap。

--

@xiaottt 也可能是黑 VS 的?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/558982

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX