如下代码,我在 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 个字节的堆内存,创建了又销毁,不应该出问题才对啊。
大家有什么看法?
1
wutiantong 2019-04-26 16:18:05 +08:00 12
你 f 里面分配的 20000 字节从未释放过
|
2
wutiantong 2019-04-26 16:18:55 +08:00 4
别以为名字都叫 p 的就是同一个变量了。。。
|
3
kizunai 2019-04-26 16:19:46 +08:00 via iPhone 1
楼上正解
|
4
downdowndown30 2019-04-26 16:20:38 +08:00 via Android 1
@wutiantong 那 for 循环里的 delete 干了什么?
|
5
lhx2008 2019-04-26 16:20:42 +08:00 2
我猜一下,f 的时候指针复制了一份,所以你把 char[2]删除了,但是出了 f 函数,旧的 p 指向的地址不变
|
6
Wangjl OP @wutiantong 为什么呢? 我 p 用的是指针啊,而且我调试的时候,看内存发现确实被释放掉了啊
|
7
downdowndown30 2019-04-26 16:21:25 +08:00 via Android 2
@wutiantong 哦。。。明白了,感谢
|
8
linxiaoziruo 2019-04-26 16:21:34 +08:00 2
引用传递和值传递
|
9
superzou 2019-04-26 16:23:02 +08:00 via Android 1
f 函数里面 p 一直都没有释放过。
|
10
downdowndown30 2019-04-26 16:24:35 +08:00 via Android 1
@Wangjl 你看看 f 里的 p 和 for 里的 p 在栈里是不是同一个对象
|
11
wutiantong 2019-04-26 16:25:12 +08:00 1
@downdowndown30 那个 delete 操作了“野指针”,严格来说是 UB 了
|
12
maxco292 2019-04-26 16:26:51 +08:00 2
正确做法:void f(char*& p)
|
13
Wangjl OP 搞不懂了,我 vs 里跟踪的时候,发现在 for 循环里的 p 遍历的地址,和 f 函数里,重新分配的地址是一样的。
|
14
bb123 2019-04-26 16:33:11 +08:00 1
1.double free
2.值传递与指针传递 |
15
wutiantong 2019-04-26 16:36:14 +08:00 1
@Wangjl 重新分配的地址一样没什么好奇怪的,它可能会一样也可能会不一样,一样的时候就无事发生,不一样的时候程序就可能会挂掉。
|
16
GPIO 2019-04-26 16:36:55 +08:00 1
值传递都是拷贝一份再操作的
|
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 3
@wutiantong 内存分页机制,一般 4k,4k 以内,有较大概率分到相同地址,大于 4k 这个概率就小很多了
|
22
AngryMagikarp 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; } |
23
nonkr 2019-04-26 17:42:31 +08:00 via iPhone 1
你需要传入两重指针,单重指针是不能这么操作的
|
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 19: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
传值,传引用,传指针好好复习一下,你这里是传值的
|