unsigned char变量不会递增

时间:2018-01-04 12:56:21

标签: c++

我遇到了一个奇怪的行为。在我的代码中,一个变量递减,但不递增,因此我的算法不起作用。变量名称为blocksAvailable,它在Chunk类中定义,使用Chunk::init方法启动,使用Chunk::allocate方法递减,并且必须使用Chunk::deallocate方法递增。因此,只有两个地方提到了这个变量 - allocatedeallocate方法。在一个地方它会减少(并且它可以工作)而在其他地方它会增加并且它不起作用。这是完全最小化和可重现的代码:

#include <cstddef>
#include <iostream>
#include <vector>

using uchar = unsigned char;

class Chunk 
{
private:    

    friend class FixedAllocator;

    void init(size_t blockSize, uchar blocks);

    void release();

    void* allocate(size_t blockSize);

    void deallocate(void* p, size_t blockSize);

    inline bool hasBlock(void* p, size_t chunkLen) const
    {
        uchar * pc = static_cast<uchar*>(p);
        return (pData <= pc) && (pc <= (pData + chunkLen));
    }

    inline bool releasable(uchar numBlocks) const
    {
          return blocksAvailable == numBlocks;    
    }

    uchar* pData;

    uchar firstAvailableBlock, blocksAvailable;    
};

void Chunk::init(size_t blockSize, uchar blocks)
{
     // for n of Ts it will allocate n * sizeof(T) memory
    pData = new uchar[blockSize * blocks];
    firstAvailableBlock = 0;
    blocksAvailable = blocks;
    uchar i = 0;
    uchar* p = pData;
    // used by allocate method to move forward firstAvailableBlock 
    for (; i != blocks; p += blockSize) 
    {
          *p = ++i;
    }
}

void Chunk::release()
{
    ::operator delete(pData);
}

void* Chunk::allocate(size_t blockSize)
{
     if (!blocksAvailable) return 0;
     // move firstAvailableBlock one block ahead
    uchar* pResult = pData + firstAvailableBlock * blockSize;
    firstAvailableBlock = *pResult;
    --blocksAvailable;
    std::cout << "blocksAvailable after allocate " << blocksAvailable << std::endl;
    return pResult;
}

void Chunk::deallocate(void* p, size_t blockSize)
{
    uchar* toRelease = static_cast<uchar*>(p);
    // find last but one available block
    firstAvailableBlock = static_cast<uchar>((toRelease - pData) / blockSize);
    ++blocksAvailable;
    std::cout << "blocksAvailable after deallocate " << blocksAvailable << std::endl;
}

class FixedAllocator 
{
private:
    size_t blockSize;
    uchar blocks;
    using Chunks = std::vector<Chunk>;
    Chunks chunks;
    Chunk* allocChunk;
public:
    FixedAllocator();
    ~FixedAllocator();
    void init(size_t blockSize, size_t pageSize);
    const int blockOwner(void* p) const;
    void * allocate();
    void deallocate(void* p);
};

FixedAllocator::FixedAllocator()
    :blockSize(0),
    blocks(0),
    chunks(0),
    allocChunk(nullptr)
{
}

FixedAllocator::~FixedAllocator()
{
    Chunks::iterator it;
    for (it = chunks.begin(); it != chunks.end(); ++it)
    {
        it->release();    
    }
}

void FixedAllocator::init(size_t blockSize_, size_t pageSize)
{
     blockSize = blockSize_;
    size_t numBlocks = pageSize / blockSize;
    blocks = static_cast<uchar>(numBlocks);
}

const int FixedAllocator::blockOwner(void* p) const
{
    size_t chunkLen = blocks * blockSize;
    std::vector<int>::size_type i = 0, sz = chunks.size();
    for (; i < sz; i++)
    {
          if (chunks[i].hasBlock(p, chunkLen))
        {
               return i;        
        }    
    }
    return -1;
}

void* FixedAllocator::allocate()
{
     if (!allocChunk || allocChunk->blocksAvailable == 0)
    {
        Chunks::iterator i = chunks.begin();    
        for (;;++i)
        {
            if (i == chunks.end())
            {
                 // allocate memory for one more chunk
                chunks.reserve(chunks.size() + 1);
                Chunk newChunk;  
                newChunk.init(blockSize, blocks);
                // add new chunk to memory pool
                chunks.push_back(newChunk);                
                // points to new just initiated chunk
                allocChunk = &chunks.back();
                break;
            }
            if (i->blocksAvailable > 0)
            {
                 // points to chunk with available blocks
                allocChunk = &*i;
                break;            
            }                   
        }
    }
    return allocChunk->allocate(blockSize);
}

void FixedAllocator::deallocate(void* p)
{
    // TODO. Optimize. Now very bruteforce and non-efficient
    const int chunkPos = blockOwner(p); 
    if (chunkPos != -1) 
    {
           Chunk chunk = chunks[chunkPos];
           chunk.deallocate(p, blockSize);         
         // if chunk is releasable, release memory 
         if (chunk.releasable(blocks))
         {
             chunk.release();
             chunks.erase(chunks.begin() + chunkPos);
             // allocChunk may point to deleted chunk
             // so, reset it                      
             allocChunk = &chunks.back();
         } else {
             // there are free blocks in chunk
             // so, reset allocChunk for faster future allocation
             allocChunk = &chunk;    
         }         
    }    
}

int main() {
     FixedAllocator* alloc = new FixedAllocator();
     alloc->init(4, 12);
     void* p = alloc->allocate();
     void* q = alloc->allocate();
     void* r = alloc->allocate();
     alloc->deallocate(p);
     alloc->deallocate(q);
     alloc->deallocate(r);   
     return 0;
}

如您所见,我的代码中有两个调试语句。一个在递增后打印blocksAvailable值,另一个在递减后打印其值。

但是当我编译并运行我的代码时,这就是我在屏幕上的内容:

enter image description here

如您所见,blocksAvailable以值3启动,然后递减三次(三次调用allocate方法),但每次递减后(调用{{ 1}})它的值保持不变 - deallocate。它真的让我发疯,看起来像我的代码中的鬼。您可以轻松地重现它,编译和运行,如下所示:

1

我希望,有人可以帮助我找到这个幽灵出现的地方。

1 个答案:

答案 0 :(得分:2)

以下是代码中唯一可以调用Chunk::deallocate的地方:

       Chunk chunk = chunks[chunkPos];
       chunk.deallocate(p, blockSize);

第一行会复制Chunk;第二行在其上调用deallocate,其增加chunk.blocksAvailable。但chunk只是数据的副本。修改它没有持久的效果。

特别是,chunks[chunkPos]不受影响且仍包含blocksAvailable = 0