C++:位字段无法与 1 位字段一起正常工作

问题描述 投票:0回答:1

我已经编写了代码来保存一些具有像 CIOParams_b_t (参见声明)这样的结构的数据到文件并加载它们。数字字段(>1 位)从文件成功加载,但是布尔字段(1 位)加载时会出现错误,可能是随机的。

保存数据程序:

//...
        std::ofstream file;
        file.open(path, std::fstream::out|std::ios::binary|std::ofstream::trunc);
//...
        union {
            CIOParams_b_t params_b_t;
            char raw[6];
        } p_data;
        p_data.params_b_t.scr_width = params.scr_width;
        p_data.params_b_t.scr_hight = params.scr_hight;
        p_data.params_b_t.bits = static_cast<int>(params.bits);
        p_data.params_b_t.brightness = params.brightness;
        p_data.params_b_t.contrast = params.contrast;

        p_data.params_b_t.fullscr = getFlag(params.flags, FLAG_FULLSCREEN)?1:0;
        p_data.params_b_t.vsync = getFlag(params.flags, FLAG_VSYNC)?1:0;
        p_data.params_b_t.mipmap = getFlag(params.flags, FLAG_MIPMAP)?1:0;
        p_data.params_b_t.skybox = getFlag(params.flags, FLAG_SKYBOX)?1:0;

        file.write(p_data.raw, 6);
//...

加载数据程序:

//...
        std::ifstream file(path);
//...
        union {
            CIOParams_b_t params_b_t;
            char raw[6];
        } p_data;
        file.read(p_data.raw, 6);

        retVal.scr_width = p_data.params_b_t.scr_width;
        retVal.scr_hight = p_data.params_b_t.scr_hight;
        retVal.bits = static_cast<CIOParams::cbits>(p_data.params_b_t.bits);
        retVal.brightness = p_data.params_b_t.brightness;
        retVal.contrast = p_data.params_b_t.contrast;

        setFlag(&retVal.flags, FLAG_FULLSCREEN, p_data.params_b_t.fullscr==1?true:false);
        setFlag(&retVal.flags, FLAG_VSYNC, p_data.params_b_t.vsync==1?true:false);
        setFlag(&retVal.flags, FLAG_MIPMAP, p_data.params_b_t.mipmap==1?true:false);
        setFlag(&retVal.flags, FLAG_SKYBOX, p_data.params_b_t.skybox==1?true:false);

        file.close();
//...

结构声明:

struct CIOParams
{
    unsigned short int scr_width;
    unsigned short int scr_hight;
    unsigned char brightness;
    unsigned char contrast;
    enum cbits
    {
        b8 = 0,
        b16,
        b32,
        b64
    } bits;
    unsigned char flags;
    bool hasError = false;
};

struct CIOParams_b_t
{
    unsigned scr_width  : 10;
    unsigned scr_hight  : 10;
    unsigned bits       : 2;
    unsigned fullscr    : 1;
    unsigned vsync      : 1;
    unsigned brightness : 8;
    unsigned contrast   : 8;
    unsigned mipmap     : 1;
    unsigned skybox     : 1;
    unsigned __unused   : 6;
};

注意:setFlag 和 getFlag 函数运行成功。无论如何,他们在单元测试中表现良好。

c++ struct fstream
1个回答
1
投票

问题来自于字节边界的填充。所以,您认为您的“CIOParams_b_t”类型是 6 个字节长。但这并不能保证。在我的环境中,编译器将其填充为 8 字节长度:

#include <iostream>
struct CIOParams_b_t
{
    unsigned scr_width : 10;
    unsigned scr_hight : 10;
    unsigned bits : 2;
    unsigned fullscr : 1;
    unsigned vsync : 1;
    unsigned brightness : 8;
    unsigned contrast : 8;
    unsigned mipmap : 1;
    unsigned skybox : 1;
    unsigned __unused : 6;
};

int main() {
    CIOParams_b_t c;
    std::cout << sizeof(c) << '\n';

}

那么,你的函数当然就无法运行了。

您现在可能有尝试使用 8 个字节的想法,它可能会起作用。但这不是正确的解决方案。

您需要的是所谓的序列化。有许多免费的现成库可供使用。但对于您的少量数据,您也可以简单地覆盖您的类的插入器

<<
和提取
>>
运算符。

简短示例:

#include <iostream>
#include <string>
#include <fstream>

struct CIOParams_b_t
{
    unsigned scr_width : 10;
    unsigned scr_hight : 10;
    unsigned bits : 2;
    unsigned fullscr : 1;
    unsigned vsync : 1;
    unsigned brightness : 8;
    unsigned contrast : 8;
    unsigned mipmap : 1;
    unsigned skybox : 1;
    unsigned __unused : 6;

    friend std::istream& operator >> (std::istream& is, CIOParams_b_t& c) {
        unsigned tmp;
        is >> tmp; c.scr_width = tmp;
        is >> tmp; c.scr_hight = tmp;
        is >> tmp; c.bits = tmp;
        is >> tmp; c.fullscr = tmp;
        is >> tmp; c.vsync = tmp;
        is >> tmp; c.brightness = tmp;
        is >> tmp; c.contrast = tmp;
        is >> tmp; c.mipmap = tmp;
        is >> tmp; c.skybox = tmp;
        return is;
    }
    friend std::ostream& operator << (std::ostream& os, const CIOParams_b_t& c) {
        return os << c.scr_width << '\n' << c.scr_hight << '\n' << c.bits << '\n' << c.fullscr << '\n' << c.vsync << '\n'
            << c.brightness << '\n' << c.contrast << '\n' << c.mipmap << '\n' << c.skybox << '\n';
    }
};


const std::string fileName{ "tmp.txt" };
int main() {

    CIOParams_b_t c1{1,3,3,0,1,4,5,0,1,0};

    if (std::ofstream outFileStream{ fileName }; outFileStream)
        outFileStream << c1;
    else
        std::cerr << "\nError: Could not open '" << fileName << "' for writing\n";


    if (std::ifstream inFileStream{ fileName }; inFileStream) {
        CIOParams_b_t c2{};
        inFileStream >> c2;
        std::cout << c2;
    }
    else
        std::cerr << "\nError: Could not open '" << fileName << "' for reading\n";
}

© www.soinside.com 2019 - 2024. All rights reserved.