昨天在操作链表的时候看到书上写了一段释放链表内存的代码,感觉奇奇怪怪的。

1
2
3
4
5
6
current = head;
while(current != NULL)
{
free(current);
current = current->next;
}

上面的代码就是在释放了current指向的内存之后仍然使用了该内存的数据。感觉好奇怪,不知道是作者的原因还是什么。
应该这样写才对:

1
2
3
4
5
6
7
current = head;
while(current != NULL)
{
tmp = current->next;
free(current);
current = tmp;
}

不然free()可能使当前节点的内容不再可用。
后面我在stackoverflow上看到一个回答,比喻的很好:

你在某个酒店订了一个房,你入住的时候,你放了一本书在这个酒店的抽屉里,但是你走的时候,你忘了这本书。而且,你还没有把这个房间的钥匙还回去。于是,你在未来某个时候,偷偷地回来,打开这个房间的门,你看到了你的书还在里间。当然,还还可以放回别的书。因为,这个酒店管理不会在你走的时候把你留下的书清走,而且,这个酒店的管理的安保措施不是那么严格,因为他信任每一个客人都会遵守管理条例。

在这种情况下,如果你幸运的话,书还会在那里,也可能你的书已经没了。也有可能当你回去的时候,有一个人在那里正在撕你的书,或者酒店把那个抽屉都挪走并变成衣柜,或是整个酒店正在被拆除以改成了一个足球场,而你偷偷摸摸进到施工现场的时候被炸死。
也就是说即使我释放了当前指针指向的内存,我也可以通过同样的指针访问该部分内容,但是内容就没有保障了,所谓some random memory area。这就好像我和系统之间的一个协议,我不会偷着回来我的房间,但是我的确偷偷的这么做了,也就是没有履行协议。
后面有个高票的回答也很好:

What you’re doing here is simply reading and writing to memory that used to be the address of a. Now that you’re outside of foo, it’s just a pointer to some random memory area. It just so happens that in your example, that memory area does exist and nothing else is using it at the moment. You don’t break anything by continuing to use it, and nothing else has overwritten it yet. Therefore, the 5 is still there. In a real program, that memory would be re-used almost immediately and you’d break something by doing this (though the symptoms may not appear until much later!)

When you return from foo, you tell the OS that you’re no longer using that memory and it can be reassigned to something else. If you’re lucky and it never does get reassigned, and the OS doesn’t catch you using it again, then you’ll get away with the lie. Chances are though you’ll end up writing over whatever else ends up with that address.

Now if you’re wondering why the compiler doesn’t complain, it’s probably because foo got eliminated by optimization. It usually will warn you about this sort of thing. C assumes you know what you’re doing though, and technically you haven’t violated scope here (there’s no reference to a itself outside of foo), only memory access rules, which only triggers a warning rather than an error.

In short: this won’t usually work, but sometimes will by chance.

Comments

2015-08-22