【问题标题】:C++ Program crashes when reading binary data from file of X length从 X 长度的文件中读取二进制数据时 C++ 程序崩溃
【发布时间】:2013-07-01 17:22:21
【问题描述】:

我正在尝试从文件中读取二进制数据,这是我的文件结构:

#define SIGNATURE_LENGTH 3
#define VERSION_LENGTH 2
#define DATACOUNT_LENGTH 4
#define COMPRESS_LENGTH 1
#define FORMAT_LENGTH 2
#define DATALENGTH_LENGTH 4

const unsigned char resSignature[SIGNATURE_LENGTH] = { 0x52, 0x45, 0x53 };
const unsigned char resVersion[VERSION_LENGTH] = { 0x01, 0x00 };

const unsigned char isCompressed[COMPRESS_LENGTH] = { 0x01 };
const unsigned char notCompressed[COMPRESS_LENGTH] = { 0x00 };

// Data Formats:
const unsigned char dataUnknown[FORMAT_LENGTH] = { 0x00, 0x00 };
const unsigned char dataXML[FORMAT_LENGTH] = { 0x01, 0x00 };


// Define header structure for resource file
struct ResHeader
{
    unsigned char signature[SIGNATURE_LENGTH];
    unsigned char version[VERSION_LENGTH];
};

// Define data structure for resource file
struct ResData
{
    unsigned char compressed[COMPRESS_LENGTH];
    unsigned char dataFormat[FORMAT_LENGTH];
    unsigned char dataLength[DATALENGTH_LENGTH];
    unsigned char *data;
};

我的班级使用:

std::fstream File;

// Resource file makeup
ResHeader  header;
unsigned char dataCount[DATACOUNT_LENGTH];

// Vector to contain resource file data
std::vector<ResData> ResourceData;

当我尝试读取文件时程序崩溃:

int ResourceFile::LoadFile()
{
    File.open("blah.dat", std::ios::in | std::ios::binary);

    // Read header
    File.read((char*) header.signature, SIGNATURE_LENGTH);
    File.read((char*) header.version, VERSION_LENGTH);
    if(!VerifyHeader())
    {
        File.close();
        return HEADER_INCORRECT;
    }
    File.read((char*) dataCount, DATACOUNT_LENGTH);
    long fileCount = unsignedCharArrayToLong(dataCount);
    for(long i = 0; i < fileCount; ++i)
    {
        ResData tmp;
        File.read((char*) tmp.compressed, COMPRESS_LENGTH);
        File.read((char*) tmp.dataFormat, FORMAT_LENGTH);
        File.read((char*) tmp.dataLength, DATALENGTH_LENGTH);
        File.read((char*) tmp.data, unsignedCharArrayToLong(tmp.dataLength));
        ResourceData.push_back(tmp);
    }
    File.close();
    return SUCCESS;
}

程序崩溃就行了:

File.read((char*) tmp.data, unsignedCharArrayToLong(tmp.dataLength));

文件中数据的长度为282,也就是读入tmp.dataLength;所以这个数字是准确的。数据也使用easy zlib压缩:http://www.firstobject.com/easy-zlib-c++-xml-compression.htm

任何关于我做错了什么或我可以做得更好的建议/帮助将不胜感激。 谢谢。

【问题讨论】:

  • 检查你的文件是否为空
  • 检查tmp.data是否为空(或未定义)...
  • unsignedCharArrayToLong(tmp.dataLength) 的结果是什么?另外,你在哪里分配data
  • if(!File) 不会出错,if(tmp.data == NULL) 也不会出错
  • 通过“不分配” tmp.data,它指向内存中的任意位置。由于在 32 位内存架构中大约有 40 亿个这样的位置,并且您的程序最多占用几兆字节 - 其中大部分是不可写的,因此您的“任意”内存地址的可能性大约为千分之一“坏”,导致它崩溃。这有点像在背后扔飞镖。如果你很幸运,你会击中飞镖板,如果你非常幸运,你会击中 20 高音。大多数时候,你不会击中墙以外的任何东西......

标签: c++ file binary fstream


【解决方案1】:

这个局部变量:

ResData tmp;

包含

unsigned char *data;

鉴于没有代码实际上将任何内容分配给data,它将指向内存中的某个“随机”位置。这意味着“未定义的行为”,并且考虑到平均定律和您的结果,在这种情况下,“未定义的行为”意味着您的程序崩溃,这可能比替代方案更好,或者当它出现时您会更加摸不着头脑在其他地方出错了。

你可能想要这样的东西(在阅读dataLength之后):

size_t len = unsignedCharArrayToLong(tmp.dataLength); 

tmp.data = new unsigned char[len];
File.read((char*) tmp.data, len);

稍后,不要忘记释放数据。或者更好的是,使用std::vector,而不是调用new,而是使用data.resize(len); 并使用tmp.data.data() 来获取File.read(...) 中的缓冲区地址。这样,您就不需要记住释放任何东西,因为std::vector 会为您完成。

【讨论】:

  • 太棒了,效果很好!非常感谢。当我不再需要它时,我一定会删除它。我考虑只使用一个向量来读取所有数据并从中提取我需要的东西,现在只是想了解这一切是如何工作的。感谢您的建议,并且一定会考虑该路线:)
  • @thomas 你绝对应该考虑使用vector。与常见的误解相反,在 C++ 中你几乎不应该使用 new 并且从不 使用 delete:RAII 可以自动管理内存。 vector 或其他 STL 容器只是 RAII 的一种形式,例如智能指针和其他代替您管理资源的东西。
  • @syam 是的,我喜欢向量,并且比数组有更多的经验。但正如我一直在谷歌上搜索如何做二进制文件一样,大部分都是按照我在这里的方式完成的。但是一个问题是,在很多代码中,在析构函数中,通常都有空向量的代码,遍历它并每次都执行删除,那么这是否不必要? vector被销毁或超出范围时会自动删除吗?
  • 如果向量中存储了指向某些数据的指针,是的,你需要删除内容。如果您要存储 shared_pointer&lt;T&gt;unique_ptr&lt;T&gt; 或类似的东西,那么不,您不需要这样做。 (在这种情况下,您存储的是一个普通字符,不需要删除)。
猜你喜欢
  • 1970-01-01
  • 2021-11-10
  • 1970-01-01
  • 1970-01-01
  • 2019-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-02
相关资源
最近更新 更多