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