将字节数组转换为标准数据类型

Posted

技术标签:

【中文标题】将字节数组转换为标准数据类型【英文标题】:Convert byte array into std data type 【发布时间】:2014-11-28 13:10:15 【问题描述】:

嗯,你好。我正在尝试编写一个自定义堆栈内存分配器来帮助我的游戏编程,但遇到了问题。所以,假设我的分配器有一个 char* 缓冲区,我想为 int 获取一些内存:

class MemoryStack

public:
MemoryStack(unsigned size)

mSize=size;
mTop=0;
mBuffer = new(std::nothrow) char [size];


char* allocate(unsigned size)

if (mTop + size > mSize)
return nullptr;
char* out = mBuffer+mTop;
mTop+=size;
return out;


private:
char* mBuffer;
unsigned mTop;
unsigned mSize;
;

int main ()

MemoryStack stack(1024);
int testval = 6;
int* ptr = (int*)stack.allocate(sizeof(int));
*ptr = testval;
std::cout<<*ptr;

现在,这可以正常工作,并打印出 6。但是,当我尝试以下操作时:

int main ()

MemoryStack stack(1024);
std::string str = "HELLO :p";
std::string* strptr = (std::string*)stack.allocate(sizeof(std::string));
*strptr = str ;
std::cout<<*strptr;

...这给了我一个 Bad Ptr 问题和一个分段错误,使我的程序崩溃。谁能解释这是为什么?这可能是因为某些运算符 = 重载吗?有什么方法可以安全地处理这个问题吗?谢谢!

编辑: 以下代码是我收到帮助后的当前最终实现 - 它没有经过彻底测试,但似乎按预期工作。发现有bug我自然会修改,不过为了帮助有兴趣的朋友,这里就放上来^_^大家可以随意使用,虽然算不上计算机科学的巅峰。

class MemoryStack;

/**Serves as a bookmark for the memory stack in order to allow to clear only part of the memory.*/
class MemoryBookmark

private:

    /**Private constructor may only be called by the memory stack object.*/
    MemoryBookmark(unsigned value)
    
        mBookmark = value;
    

    unsigned mBookmark;

public:
    friend class MemoryStack;

    /**Returns the index of the position that will be the new stack top pointer.*/
    decltype(mBookmark) getValue() const
    
        return mBookmark;
    
;

/**Acts as a basic memory stack to help reduce allocation costs, as well as add to the fun! Use with care, as destructors must be called manually.*/
class MemoryStack

private:
    char* mBuffer;
    size_t mTop;
    size_t mCapacity;
    size_t mAlignment;

public:

    /**Initialises the class, reserving _capacity_ bytes for use. It can not be resized for efficiency purposes.*/
    MemoryStack(unsigned capacity)
    
        mCapacity = capacity;
        mTop = 0;
        mBuffer = new(std::nothrow) char[capacity];
        mAlignment = 4;
    

    /**Frees the memory, invalidating all internal memory. Doesn't call destructors.*/
    ~MemoryStack()
    
        if (mBuffer)
            delete[] mBuffer;
    

    /**Creates an instance of the given type with Args if possible, using aligned internal memory.*/
    template <typename T, typename... Args>
    void create(T*& ptr, Args&&... args)
    
        ptr = (T*)allocate(sizeof(T));
        if (!ptr)
            return;
        else
            new (ptr)T(std::forward<Args>(args)...);
    

    /**Calls the destructor of the pointer. Must be used if destruction important.*/
    template<typename T>
    void destroy(T* ptr)
    
        ptr->~T();
    

    /**Allocates a piece of memory for use.*/
    void* allocate(size_t amount)
    
        size_t bt = (size_t)(mBuffer + mTop);
        size_t alignoffset = mAlignment - (bt & (mAlignment - 1));
        alignoffset = alignoffset == mAlignment ? 0 : alignoffset;
        size_t size = amount + alignoffset;
        if (size + mTop > mCapacity)
            return nullptr;
        else
        
            mTop += size;
            return (void*)(bt + alignoffset);
        
    

    /**Returns the amount of memory used.*/
    size_t size() const
    
        return mTop;
    

    /**Returns the size of the memory reserved for use.*/
    size_t capacity() const
    
        return mCapacity;
    

    /**Returns the number of bytes remaining for allocation.*/
    size_t remaining() const
    
        return mCapacity - mTop;
    

    /**Checks whether the internal memory was allocated successfully.*/
    bool isValid() const
    
        return mBuffer != nullptr;
    

    /**Creates a 'bookmark' which can be used to clear the stack until a given point.*/
    MemoryBookmark createBookmark() const
    
        return MemoryBookmark(mTop);
    

    /**Resets the stack. All data inside may now be overwritten. Doesn't call destructors.*/
    void reset()
    
    mTop = 0;
    

    /**Resets the stack up to a given bookmark. Again, no destructors called!*/
    void resetToBookmark(const MemoryBookmark bookmark)
    
        mTop = bookmark.getValue();
    

    /**Sets the alignment of the reservations in memory.*/
    void setAlignment(size_t alignment)
    
        mAlignment = alignment;
    

    /**Returns the currently used alignment.*/
    decltype(mAlignment) getAlignment() const
    
        return mAlignment;
    
;

/**Test class.*/
class Test

public:
    Test(int val)
    
        v = val;
        std::cerr << "Constructor\n";
    

    ~Test()
    
        std::cerr << "Destructor";
    

    int v;
;

/**Test it! XD*/
int main()

    using namespace std;
    
        MemoryStack stack(4096);

        Test* test=nullptr;
        int* i1, *i2;
        char* c1, *c2;
        stack.create(test,3);
        stack.create(i1, 2);
        stack.create(c1, 'a');
        stack.create(i2, 3);
        stack.create(c2, 'm');
        stack.destroy(test);
        stack.reset();
    

    cin.get();

【问题讨论】:

只是想知道是否真的有必要自己动手。是不是有一些你可以使用的 FOSS 让你继续玩有趣的东西?有很多免费的游戏框架和内存分配器,它们是完全开发的、经过良好测试和支持的。即使你可以做得更好(没有冒犯),你愿意花时间这样做吗? 啊,你可能是对的......但我首先是学生,知道如何做这些事情感觉很好^_^另外,这会让我有更多的控制权,并允许我在需要时扩展这些结构。 【参考方案1】:

未初始化的内存区域不是std::string。您必须在区域中构造一个对象,并像这样放置 new:

std::string *strptr = (std::string*) stack.allocate(sizeof(std::string));
new (strptr) std::string;

值得考虑在中心位置执行此操作,例如MemoryStack 中的成员函数模板。

编辑:

我之前忘了提这个,很遗憾,因为它非常重要:

如果你用placement new 构造对象,你也必须手动销毁它们。他们不会自己做,因为他们没有自动存储持续时间,而且你不能使用delete,因为内存不是用new分配的。语法非常简单:

strptr->~string();

最好将这两个部分都作为MemoryStack 的一部分,例如:

class MemoryStack 
  ...

  template<typename T, typename... Args>
  T *create(Args&&... args) 
    T *ptr = allocate(sizeof(T));
    try 
      new(ptr) T(std::forward<Args>(args)...);
     catch(...) 
      deallocate(ptr);
      throw;
    

    return ptr;
  

  template<typename T>
  void destroy(T *ptr) 
    ptr->~T();
    deallocate(ptr);
  

  ...
;

以后写

std::string *strptr = stack.create<std::string>("foo");

...

stack.destroy(strptr);

左右。专业提示:为自己构建一个删除器,您可以将其与 std::unique_ptrstd::shared_ptr 结合使用,以简化异常安全性。

【讨论】:

天哪,谢谢!这似乎表现得很漂亮。我以前没有使用过新的展示位置,因此必须调查性能影响以及如何正确使用它。但是我现在才试了一下,确实有效! 哦,我明白了。谢谢!我接受了您的建议,并修改了我的代码以允许使用适当的析构函数。遗憾的是,我还没有完全熟悉高级 C++11 功能,所以我选择保持内存堆栈相当简单,尽管对于我的目的来说已经足够了。我将把我的最终实现放在上面的编辑中。【参考方案2】:

您正在尝试分配 std::string 的大小 std::string* strptr = (std::string*)stack.allocate(sizeof(std::string));

你的意思可能是 std::string* strptr = (std::string*)stack.allocate(str.size()));

Wintermute 是一个不错的主意,但它可能无法满足您的需求 一种。您的 allocate 函数将返回 nullptr 湾。 “新”将分配一个新的内存区域,而不是在你的“堆”中

虽然可能不会崩溃...

Draknghar:(我太年轻了要添加 cmets) 改变主意。它会起作用的。但你没有设置 mTop ...... 这样做似乎有些不对劲。你已经有了你的数组并且你试图在其中分配新的内存?为什么?只需将您的字符串复制到其中,然后设置 mTop。

【讨论】:

我刚试了一下,好像还可以。我认为他们使用的新的似乎是新的安置。因此,它应该在我的内存堆栈中。不过谢谢!

以上是关于将字节数组转换为标准数据类型的主要内容,如果未能解决你的问题,请参考以下文章

java中,如何将double类型数据转换为16进制字符串或者是16进制字节数组

将Long类型字节大小数据转换成标准的视频大小格式

PHP当中如何将数组当中的字符串数据类型转化为数值类型?

javascript中如何将获得的整型数值转换为字节数组

将字节数组转换为java类型

Java字节数组到Javascript类型数组