如下代码,我在 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 个字节的堆内存,创建了又销毁,不应该出问题才对啊。
大家有什么看法?
|  |      1wutiantong      2019-04-26 16:18:05 +08:00  12 你 f 里面分配的 20000 字节从未释放过 | 
|  |      2wutiantong      2019-04-26 16:18:55 +08:00  4 别以为名字都叫 p 的就是同一个变量了。。。 | 
|  |      3kizunai      2019-04-26 16:19:46 +08:00 via iPhone  1 楼上正解 | 
|      4downdowndown30      2019-04-26 16:20:38 +08:00 via Android  1 @wutiantong 那 for 循环里的 delete 干了什么? | 
|  |      5lhx2008      2019-04-26 16:20:42 +08:00  2 我猜一下,f 的时候指针复制了一份,所以你把 char[2]删除了,但是出了 f 函数,旧的 p 指向的地址不变 | 
|  |      6Wangjl OP @wutiantong  为什么呢? 我 p 用的是指针啊,而且我调试的时候,看内存发现确实被释放掉了啊 | 
|      7downdowndown30      2019-04-26 16:21:25 +08:00 via Android  2 @wutiantong 哦。。。明白了,感谢 | 
|  |      8linxiaoziruo      2019-04-26 16:21:34 +08:00  2 引用传递和值传递 | 
|      9superzou      2019-04-26 16:23:02 +08:00 via Android  1 f 函数里面 p 一直都没有释放过。 | 
|      10downdowndown30      2019-04-26 16:24:35 +08:00 via Android  1 @Wangjl 你看看 f 里的 p 和 for 里的 p 在栈里是不是同一个对象 | 
|  |      11wutiantong      2019-04-26 16:25:12 +08:00  1 @downdowndown30 那个 delete 操作了“野指针”,严格来说是 UB 了 | 
|  |      12maxco292      2019-04-26 16:26:51 +08:00  2 正确做法:void f(char*& p) | 
|  |      13Wangjl OP 搞不懂了,我 vs 里跟踪的时候,发现在 for 循环里的 p 遍历的地址,和 f 函数里,重新分配的地址是一样的。 | 
|      14bb123      2019-04-26 16:33:11 +08:00  1 1.double free 2.值传递与指针传递 | 
|  |      15wutiantong      2019-04-26 16:36:14 +08:00  1 @Wangjl 重新分配的地址一样没什么好奇怪的,它可能会一样也可能会不一样,一样的时候就无事发生,不一样的时候程序就可能会挂掉。 | 
|  |      16GPIO      2019-04-26 16:36:55 +08:00  1 值传递都是拷贝一份再操作的 | 
|  |      17Wangjl OP 懂了,感谢各位的回复。 这让我这个初学者难了好久,一直想不通。现在经各位指点已经想通了,应该用引用,否则会变成值传递。 造成二次释放。c++真的感觉比其他语言好难啊,坑比较多哦。 | 
|      18dfjslkjdf      2019-04-26 16:43:20 +08:00 @wutiantong  大哥好眼力 | 
|  |      19Wangjl OP | 
|  |      20402124773      2019-04-26 17:04:44 +08:00 学一下智能指针 | 
|      21binlaten      2019-04-26 17:30:31 +08:00 via Android  3 @wutiantong 内存分页机制,一般 4k,4k 以内,有较大概率分到相同地址,大于 4k 这个概率就小很多了 | 
|  |      22AngryMagikarp      2019-04-26 17:36:58 +08:00  1 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; } | 
|      23nonkr      2019-04-26 17:42:31 +08:00 via iPhone  1 你需要传入两重指针,单重指针是不能这么操作的 | 
|  |      24Wangjl OP @nonkr  @AngryMagikarp @wutiantong @bb123 一语道破玄机, 就是这样的。 因为指针传递,相当于值传递,进去的 p 已经不是原来的 p 指针了,而是局部变量的 p, 当在函数中释放一次后,新申请的空间实际上是给了局部变量的 p,而局部变量随着函数的销毁而销毁,因此在外部 再次 delete 的时候,相当于进行了二次 free,所以会出问题。v 站大神真多啊,学到了。 | 
|  |      25Wangjl OP 解决方法就是用双重指针或者引用 | 
|  |      26stephenyin      2019-04-26 19:17:31 +08:00 这是一款出镜率极高的 C/C++ 基础面试题. | 
|  |      27dabaibai      2019-04-26 19:48:18 +08:00 C 代码 这不是 C++ | 
|      29iwong0exv2      2019-04-26 20:21:44 +08:00 via Android 去面华为吧! 当年我去面的时候给了道环链表检测的题,要求实现 bool test(const LIST_ENTRY *p),p 是链表头。我上来就是 p=p->next;。面试官说我这样直接改指针,会影响外面的链表。我说这种编码风格可能不太规范,但真不会修改到外面的链表。他说这传的是指针啊,你知道指针的用法吗? 后面当然没通过。 | 
|      30GeruzoniAnsasu      2019-04-26 20:34:03 +08:00 我在想 new char[2]的那个 p 被 delete 了两次为啥不会崩,1000 次 都没崩 | 
|      31smdbh      2019-04-26 20:39:55 +08:00 基础了吧,不应该了 | 
|  |      32huaouo      2019-04-26 20:46:59 +08:00 via Android @wutiantong 那个大概叫 空悬指针? | 
|      33yippees      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; } //和神奇无关 | 
|      34hihibin      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 地址直接操作了,会报错吧。 | 
|      35radiolover      2019-04-26 23:16:59 +08:00 via Android 国内互联网越来越水是有原因的 | 
|      36jackmod      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。 | 
|      37xiaottt      2019-04-26 23:27:34 +08:00 骗铜币的吧。。。233333333 | 
|      39lynskylate      2019-04-26 23:39:40 +08:00 via Android ....就不该这么写,同一作用域分配的内存尽量在同一作用域内 shifang | 
|  |      40yuikns      2019-04-27 00:07:35 +08:00 | 
|  |      42WANGJIEKE      2019-04-27 04:19:11 +08:00 我寻思着这不是 double delete 吗。。。double delete 在我电脑上跑一次就炸的,不知道为什么你这跑 1000 次不出问题 | 
|      43missdeer      2019-04-27 08:08:54 +08:00 一楼正解 | 
|      44zwh2698      2019-04-27 08:15:29 +08:00 via Android 人生到处都是坑,且行且小心!前途未知概因不曾顿悟。 | 
|  |      45huluhulu      2019-04-27 08:49:16 +08:00 via iPhone @iwong0exv2 你这个会影响。你和楼主的案例不一样。 | 
|  |      46Wangjl OP 这可能和 vs 有关,我现在又无法重现了,之前 for1000 次都没问题,可昨天晚上,我 for1 次就不行了。 但我做过测试,如果函数内的 p 的本身地址和函数外的 p 的地址一样的话,就不会出问题,我估计我之前那是偶然现象,可能是 vs 的问题。 只要函数内的指针地址和外面的不一样,那一定会挂,因为二次释放。 | 
|  |      47Wangjl OP #include <stdio.h> void f(char* p) { printf("f 函数中 p 指针本身的地址是: %x\n\n", &p); printf("f 函数中 p 指针里面存放的地址是: %x\n\n", *p); printf("释放 f 函数中 p 指针里面存放的地址 %x 指向的内存空间\n\n", *p); delete[] p; p = new char[20000]; } int main() { for (int i = 0; i < 1000; i++) { char *p = new char[2]; printf("外部 p 指针本身的地址是: %x\n\n", &p); printf("外部 p 指针里面存放的地址是: %x\n\n", *p); f(p); printf("释放外部 p 指针里面存放的地址 %x 指向的内存空间\n\n", *p); delete[] p; } getchar(); return 0; } 以上代码就可以看出原因。 | 
|      48iwong0exv2      2019-04-27 13:59:11 +08:00 via Android @huluhulu 兄弟你也是花厂的? | 
|  |      49huluhulu      2019-04-27 15:35:33 +08:00 via iPhone @iwong0exv2 不是 | 
|  |      50leido      2019-04-27 16:10:48 +08:00 via Android 用 f 的参数 p 指针用引用才对 | 
|      51iwong0exv2      2019-04-27 16:14:29 +08:00 via Android @huluhulu 哦。如果写成 p->next=p;的话,是会修改链表,但反过来写只是利用同一个指针来做遍历,所以并不会修改链表本身。其实我这个解释有点多余,代码本身已经很清楚了。 | 
|  |      52huluhulu      2019-04-27 16:59:13 +08:00 via iPhone @iwong0exv2 你是对的 | 
|      53darknoll      2019-04-28 09:10:06 +08:00 char*& p | 
|      54Chenamy2017      2019-04-28 09:23:16 +08:00 一楼正解,看来指针还需要再好好学习下了 | 
|      55tkhmy      2019-04-28 09:50:56 +08:00 via Android 传值,传引用,传指针好好复习一下,你这里是传值的 |