使用LocalAlloc()访问冲突

时间:2010-05-10 22:15:51

标签: c++ windows-mobile memory-management

我有一个使用需要使用LocalAlloc()的API的Visual Studio 2008 Windows Mobile 6 C ++应用程序。为了让我的生活更轻松,我创建了一个在内部使用LocalAlloc()的标准分配器的实现:

/// Standard library allocator implementation using LocalAlloc and LocalReAlloc 
/// to create a dynamically-sized array. 
/// Memory allocated by this allocator is never deallocated. That is up to the
/// user.
template< class T, int max_allocations > 
class LocalAllocator
{
public:
    typedef T         value_type;
    typedef size_t    size_type;
    typedef ptrdiff_t difference_type;
    typedef T*        pointer;
    typedef const T*  const_pointer;
    typedef T&        reference;
    typedef const T&  const_reference;

    pointer address( reference r ) const { return &r; };
    const_pointer address( const_reference r ) const { return &r; };

    LocalAllocator() throw() : c_( NULL )
    {
    };

    /// Attempt to allocate a block of storage with enough space for n elements
    /// of type T. n>=1 && n<=max_allocations.
    /// If memory cannot be allocated, a std::bad_alloc() exception is thrown.
    pointer allocate( size_type n, const void* /*hint*/ = 0 )
    {
        if( NULL == c_ )
        {   
            c_ = LocalAlloc( LPTR, sizeof( T ) * n );
        }
        else
        {
            HLOCAL c = LocalReAlloc( c_, sizeof( T ) * n, LHND );
            if( NULL == c )
                LocalFree( c_ );
            c_ = c;
        }
        if( NULL == c_ )
            throw std::bad_alloc();
        return reinterpret_cast< T* >( c_ );
    };

    /// Normally, this would release a block of previously allocated storage.
    /// Since that's not what we want, this function does nothing.
    void deallocate( pointer /*p*/, size_type /*n*/ )
    {
        // no deallocation is performed. that is up to the user.
    };

    /// maximum number of elements that can be allocated
    size_type max_size() const throw() { return max_allocations; };

private:
    /// current allocation point
    HLOCAL c_;
}; // class LocalAllocator

我的应用程序在std :: vector&lt;&gt;

中使用该分配器实现
#define MAX_DIRECTORY_LISTING 512

std::vector< WIN32_FIND_DATA, 
    LocalAllocator< WIN32_FIND_DATA, MAX_DIRECTORY_LISTING > > file_list;

WIN32_FIND_DATA find_data = { 0 };
HANDLE find_file = ::FindFirstFile( folder.c_str(), &find_data );
if( NULL != find_file )
{
    do 
    {
        // access violation here on the 257th item.
        file_list.push_back( find_data );
    } while ( ::FindNextFile( find_file, &find_data ) );

    ::FindClose( find_file );
}

// data submitted to the API that requires LocalAlloc()'d array of WIN32_FIND_DATA structures
SubmitData( &file_list.front() );

在添加到矢量&lt;&gt;的第257项上,应用程序因访问冲突而崩溃:

Data Abort: Thread=8e1b0400 Proc=8031c1b0 'rapiclnt'
AKY=00008001 PC=03f9e3c8(coredll.dll+0x000543c8) RA=03f9ff04(coredll.dll+0x00055f04) BVA=21ae0020 FSR=00000007
First-chance exception at 0x03f9e3c8 in rapiclnt.exe: 0xC0000005: Access violation reading location 0x01ae0020.
调用{p> LocalAllocator::allocaten=512成功,LocalReAlloc()成功。实际的访问冲突异常发生在std :: vector&lt;&gt;中LocalAllocator::allocate电话后的代码:

     0x03f9e3c8    
     0x03f9ff04    
>    MyLib.dll!stlp_std::priv::__copy_trivial(const void* __first = 0x01ae0020, const void* __last = 0x01b03020, void* __result = 0x01b10020) Line: 224, Byte Offsets: 0x3c    C++
     MyLib.dll!stlp_std::vector<_WIN32_FIND_DATAW,LocalAllocator<_WIN32_FIND_DATAW,512> >::_M_insert_overflow(_WIN32_FIND_DATAW* __pos = 0x01b03020, _WIN32_FIND_DATAW& __x = {...}, stlp_std::__true_type& __formal = {...}, unsigned int __fill_len = 1, bool __atend = true) Line: 112, Byte Offsets: 0x5c    C++
     MyLib.dll!stlp_std::vector<_WIN32_FIND_DATAW,LocalAllocator<_WIN32_FIND_DATAW,512> >::push_back(_WIN32_FIND_DATAW& __x = {...}) Line: 388, Byte Offsets: 0xa0    C++
     MyLib.dll!Foo(unsigned long int cbInput = 16, unsigned char* pInput = 0x01a45620, unsigned long int* pcbOutput = 0x1dabfbbc, unsigned char** ppOutput = 0x1dabfbc0, IRAPIStream* __formal = 0x00000000) Line: 66, Byte Offsets: 0x1e4    C++

如果有人能够指出我可能做错了什么,我将不胜感激。

谢谢, PaulH

3 个答案:

答案 0 :(得分:2)

我不确定这是怎么回事。分配每个下一个块时,释放当前使用的块。所以指向那里的所有指针都变得无效。我怀疑这是矢量对分配器的期望。

答案 1 :(得分:2)

我首先认为您的问题是参数LHNDLocalReAlloc() - 您通常不应该将该参数传递给该函数。

实际问题是你甚至不应该调用该函数。 vector实现重新分配自己的内存。 C ++标准分配器不提供重新分配。

你应该实现:

template<typename T> class LocalAllocator {
...
pointer allocate(sizetype s)
{
    pointer p = reinterpret_cast<pointer>(LocalAlloc(LPTR, s * sizeof(T)));

    if (NULL == p)
        throw std::bad_alloc();

    return p;
}

void deallocate(pointer p, sizetype)    
{
    LocalFree(reinterpret_cast<LHND>(p));
}
...
}

类似的东西应该有用。

您无需跟踪指针 - 它会在致电deallocate()时提供给您,这是您的API客户端vector<T>实施的责任

答案 2 :(得分:1)

要展开Igor Krivokon's answer,每次调用allocate()可能会使之前调用allocate()所返回的所有指针无效。这不是分配器应该如何工作,并且很可能导致未定义的行为:

例如,当向量需要分配新的更大内存时,它会将数据从“旧”内存复制到allocate()返回的“新”内存。然后它将销毁“旧”内存中的所有对象并释放它。

如果 old new 现在是相同的内存位置,则复制“new”对象的构造以及以下对“旧”对象的破坏将全部搞乱起来。另一方面,如果 old 不再有效(因为它已重新分配),该向量将访问无效的内存。

还有一点:分配器必须是可复制构造的。如果从分配器构造副本,然后两者都用于分配更多元素,则将在同一指针上调用LocalReAlloc()两次。因此,您需要提供一个避免此类问题的自定义复制构造函数。