向量重新分配导致malloc错误

时间:2015-07-24 12:51:25

标签: c++ vector exif

我正在开发一个从JPEG文件中读取Exif数据的应用程序。数据存储在结构中,如下所示:

struct Metadata{
    int tagID = 0;
    std::string tagIDHex;
    int ifdNo = 0; // 0=IDF0, 1= Exif, 2=GPS, 3=Interop, 4 = IFD1
    BYTE* values;
    int noValues = 0;
    long valuesStart = 0; 
    int bytesPerValue = 1;
    long dataSize = 0;  // Generally bytesPerValue x noValues
    bool usesOffset = false; 
    /*If no bytes used by values is 4 or less, last 4 bytes in field hold actual values, 
    otherwise they point to location elsewhere in file */
    BYTE fieldData[12]; // Holds IFD field

    Metadata(BYTE* data, int ifd){
        ifdNo = ifd;
        tagID = data[0] * 256 + data[1];
        tagIDHex = intToHex(tagID);
        for (int b = 0; b < 12; b++){
            fieldData[b] = data[b];
        }
        noValues = (int)((fieldData[4] * std::pow(256, 3) + fieldData[5] * std::pow(256, 2) + fieldData[6] * std::pow(256, 1)
            + fieldData[7] * std::pow(256, 0)));
        // Look up datatype size based on TIFF spec where 1= BYTE, etc. 
        bytesPerValue = getBytesPerValue(fieldData[3]);
        dataSize = noValues*bytesPerValue; 
        usesOffset = (dataSize>4);
        if (usesOffset){
            values = new BYTE[noValues]; // will get populated later
        }
    }
};

以下代码循环遍历EXIF IFD中保存的字段,并将每个字段添加到名为existingMetadataExif的向量中。

for (int f = 0; f < exifFields; f++){
    long tagAddress = exifStart + 2 + f * 12;
    Metadata m = Metadata(&file[tagAddress], 1);
    if (m.usesOffset){
        m.valuesStart = (int)(tiffStart + (m.fieldData[8] * std::pow(256, 3) + m.fieldData[9] * std::pow(256, 2) + m.fieldData[10] * std::pow(256, 1) + m.fieldData[11] * std::pow(256, 0)));
        for (int d = 0; d < (m.noValues*m.bytesPerValue); d++){
            m.values[d] = file[m.valuesStart + d];
        }
    }
    if (existingMetadataExif.size() >27){
        bool debug = true;
    }
    existingMetadataExif.push_back(m);
}

代码适用于某些文件,但我遇到了其他人的内存问题。这个问题似乎与向量的重新分配有关。所有文件最多可以处理28个元素。这似乎是向量的默认保留容量。当每个元素加起来为28时,大小和容量增加1 - 0 / 0,1 / 1,2 / 2等。当大小达到29时,向量的容量增加到42即50%增加原始容量。

虽然错误总是在第28个/第29个元素附近,但它并不完全一致。一个文件将向量容量增加到42并立即崩溃,并触发断点&#34;异常,另一个文件一碰到第28个元素就会触发崩溃。

我在代码中尝试过existingMetadataExif.reserve(42),但没有区别。

虽然看起来尺寸重新分配是触发点,但我也想知道

 values = new BYTE[noValues] 

结构内部的行。这是必需的,因为每个元数据可以包含不同数量的值,包括无,但我不会在应用程序结束之前在任何地方直接删除数组。

我在Windows 8.1上的Visual Studio 2013中进行开发,但没有使用任何MS特定代码,因为此应用程序最终将移植到iOS。

修改

只是澄清一下 - existingMetadataExif是向量,在代码中的其他地方声明,错误发生在

 existingMetadataExif.push_back(m);

    if (existingMetadataExif.size() >27){
        bool debug = true;
    }

是无关紧要的,可以忽略,我只是帮助我自己的调试尝试。

2 个答案:

答案 0 :(得分:2)

Oups,你将一个包含新分配的char数组和一个构造函数的对象推送到std容器。就像在脚下射击自己......

它可以工作,但你必须谨慎:

  • 您必须在构造函数中分配char数组:fine
  • 您必须从析构函数中解除分配
  • 您必须实现复制构造函数,在其中分配新数组并复制内容

这就是NathanOliver在评论中的来电者The Rule of Three

如果要使用C ++ 11移动语义,可以添加移动构造函数,复制原始对象数组的地址,并将指针(在原始对象中)设置为0或nullptr。这样,您可以保存数组的分配和副本。

答案 1 :(得分:2)

任何new都有风险,即使您(认为)您拥有相应的delete。在现代C ++中,您几乎不需要new

((实际上,为什么不去掉指针并改用vector<BYTE>?))

    if (usesOffset){
        values = new BYTE[noValues]; // will get populated later
    }

你应该考虑在这里使用共享指针:

 #include<memory>
 ...
 std::shared_ptr<BYTE> values;
 ....
    if (usesOffset){
        values = std::shared_ptr<BYTE> ( new BYTE[noValues], std::default_delete<BYTE[]>() );
    }

请注意这里使用的特殊删除器,因为它是一个数组。 (For more on this)。

假设编译,并且您希望确保每个Metadata拥有自己的values副本,那么您应该使用unique_ptr代替:(再次,从上面的链接)

std::unique_ptr<BYTE> values;
...
    values = std::unique_ptr<BYTE[]> ( new BYTE[noValues] ); // this
                                     // will correctly call delete[]

好消息是使用unique_ptr可能会导致代码无法编译。我说好,因为它迫使你处理复制。您可以实现完整拷贝构造函数和拷贝赋值运算符,也可以在此处使用move

    existingMetadataExif.push_back( std::move(m) );