【问题标题】:C++ malloc/realloc weird behaviorC++ malloc/realloc 奇怪的行为
【发布时间】:2018-10-22 06:00:12
【问题描述】:

我正在编写一个动态数组供我自己使用,我想用零预设。

template <class T>
dynArr<T>::dynArr()
{
rawData = malloc(sizeof(T) * 20); //we allocate space for 20 elems
memset(this->rawData, 0, sizeof(T) * 20); //we zero it!
currentSize = 20;
dataPtr = static_cast<T*>(rawData); //we cast pointer to required datatype.
}

这部分有效 - 使用 dereferencind 循环迭代 dataPtr 效果很好。零。

然而,重新分配的行为(在我看来)至少有点奇怪。首先你要看看重新分配代码:

template <class T>
void dynArr<T>::insert(const int index, const T& data)
{

    if (index < currentSize - 1)
    {
        dataPtr[index] = data; //we can just insert things, array is zero-d
    }

    else
    {
        //TODO we should increase size exponentially, not just to the element we want

        const size_t lastSize = currentSize; //store current size (before realloc). this is count not bytes.

        rawData = realloc(rawData, index + 1); //rawData points now to new location in the memory
        dataPtr = (T*)rawData;
        memset(dataPtr + lastSize - 1, 0, sizeof(T) * index - lastSize - 1); //we zero from ptr+last size to index

        dataPtr[index] = data;
        currentSize = index + 1;
    }

}

很简单,我们将数据重新分配到 index+1,并将尚未归零的内存设置为 0。

作为一个测试,我首先在这个数组的第 5 位插入了 5。预期的事情发生了 - 0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

然而,插入其他东西,比如 insert(30,30) 会给我带来奇怪的行为:

0, 0, 0, 0, 0, 5, 0, -50331648, 16645629, 0, 523809160, 57600, 50928864, 50922840, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30,

什么鬼,我这里有什么不明白的吗? realloc 不应该考虑所有 20 个先前设置的内存字节吗?这是什么魔法。

【问题讨论】:

  • 你忘了在 realloc 调用中将(index+1)sizeof(T)相乘。
  • rawData = realloc(rawData, index + 1); 如果失败,您将丢失所有数据。
  • 另外,原始内存mallocrealloc 返回不包含对象,您必须在使用placement new 分配之前创建它们。否则,您将调用未定义的行为。
  • dataPtr[index] = data; 是 wooong.. 这不是 C...

标签: c++ memory malloc realloc


【解决方案1】:

问题一:

您在调用realloc 时使用了错误的大小。将其更改为:

rawData = realloc(rawData, sizeof(T)*(index + 1)); 

如果rawDataT* 类型,首选

rawData = realloc(rawData, sizeof(*rawData)*(index + 1)); 

问题2:

下面的最后一项是不对的。

memset(dataPtr + lastSize - 1, 0, sizeof(T) * index - lastSize - 1); 

你需要使用:

memset(dataPtr + lastSize - 1, 0, sizeof(T) * (index - lastSize - 1));
                               //  ^^              ^^
                               // size      *  The number of objects 

问题3:

使用分配给dataPtr

dataPtr[index] = data;

在使用mallocrealloc 获取内存时会出现问题。 malloc 系列函数只返回原始内存。他们不初始化对象。 分配给未初始化的对象对于所有非 POD 类型都是一个问题。

问题4:

如果T 是带有虚成员函数的类型,使用memset 将内存归零很可能会导致问题。


解决所有问题的建议:

使用newdelete 会好得多,因为你在 C++ 领域。

template <class T>
dynArr<T>::dynArr()
{
   currentSize = 20;
   dataPtr = new T[currentSize];
   // Not sure why you need rawData
}

template <class T>
void dynArr<T>::insert(const int index, const T& data)
{
   if (index < currentSize - 1)
   {
      dataPtr[index] = data;
   }

   else
   {
      const size_t lastSize = currentSize;
      T* newData = new T[index+1];
      std::copy(dataPtr, dataPtr+lastSize, newData);
      delete [] dataPtr;
      dataPtr = newData;
      dataPtr[index] = data;
      currentSize = index + 1;
   }
}

请注意,建议的更改仅在 T 是默认可构造的情况下才有效。

这也将解决上面列出的问题 3 和 4。

【讨论】:

  • @Swift 这是一个有趣的问题。在什么情况下(可能是移动)分配对于归零的对象是无效的。
  • @CaptainGiraffe 如果它不是标准布局\POD 类。这也可能导致编译器出现问题。从技术上讲,除非使用放置新的位置,否则那里发生的事情是 UB。后者将在分配的内存中构造对象。这就是内存池的工作方式
  • @Swift 在一般情况下,绝对如此。我正在考虑特定的编译器行为。
  • @Swift 只要对象没有非空初始化,它是完全合法的。如果它具有非空初始化,则需要放置新的。
  • @shajduk 投射指针不会在它指向的内存中创建对象。
猜你喜欢
  • 1970-01-01
  • 2016-07-30
  • 2015-06-05
  • 2011-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多