一个有趣的代码问题

OldNewThing发布了一篇很有意思的文章http://blogs.msdn.com/oldnewthing/archive/2010/01/20/9950638.aspx

The wrong way to determine the size of a buffer

A colleague of mine showed me some code from a back-end program on a web server. Fortunately, the company that wrote this is out of business. Or at least I hope they’re out of business!

size = 16384;

while (size && IsBadReadPtr(buffer, size))

{ size--; }

高人就是高人,这个代码其实还是需要好好想一想才能明白为何有问题。

IsBadReadPtr的意思是尝试去读一个不属于自己进程的内存地址,因为指针越界在大多数情况下都是代表错误发生。但是这个函数实际上并不是那么好用,具体原因可以看OldNewThing的这篇文章http://blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx 介绍的非常详细。

回到这个问题上,在OldNewThing的文章中介绍的非常清楚“But guard page exceptions are raised only once.”,也就是说,IsBadReadPtr将只会有效一次(?need to double check),这个代码逻辑就是有问题的。

再把其中包含的问题列一下,主要参考了文章后的留言。

1)16384这个16K大小来的莫名其妙,当然不排除在某些特定软件设计中,最大就是这个大小。

2)第二个问题,IsBadReadPtr并不是那么好用。

3)这个计算得到的值未必正确。

那么我们该如何获取buffer的大小呢?

在c-faq中告诉我们,通常情况下是没有解决办法的,只能自己保存这个buffer大小。办法也很简单,我们需要申请的时候就把buffer大小记住,需要知道的时候,查找一下就知道了,或者是使用某种数据结构,把buffer的大小以及其他信息放在开头固定的一块大小就行了。

如果是windows,那么有一些特定的办法。可以用_msize来获得malloc申请的buffer。

allocated with LocalAlloc, use LocalSize. For HeapAlloc, use HeapSize. For GlobalAlloc, use GlobalSize,如果使用CoTaskMemalloc,可以先用CoGetMalloc拿到一个IMalloc接口,然后调用GetSize()

这些函数到底好用不好用,还是得靠你自己多测试。最简单牢靠的办法还是自己保存这个size信息。

《一个有趣的代码问题》有2个想法

  1. 这段程序根本不能实现目的,最直接的原因就是IsBadReadPtr只是判断是否有权限可以读取这段buffer.
    我以不同方式试了一下这段程序,根本不能准确得不到buffer的size。
    因此,我很有疑惑,错误不是很明显的吗?难道这段代码还有可能得到buffer的大小?
    如果的确有可能可以准确得到buffer的大小,那就证明它有运行的可能,和改进的可能(姑且不考虑性能)

    总的来说,我只是有个疑惑:难道这段程序有可能跑对?

发表评论