与STD :: BITSET的工会成员的结构填充

Structure padding with union members of std::bitset

本文关键字:成员 结构 填充 BITSET STD      更新时间:2023-10-16

在我解决了这个问题之后,我继续扩展了我的代码版本,以将我以前的模板版本中的数据字段的工会与此版本合并在一起,我有这个到目前为止:

main.cpp

#include <iostream>
#include <type_traits>
#include "Register.h"
int main() {
    using namespace vpc;
    std::cout << std::boolalpha;
    std::cout << "std::bitset<64> is trivially copyable "
        << std::is_trivially_copyable<std::bitset<64>>::value << 'n'
        << "QWord is trivially copyable "
        << std::is_trivially_copyable<QWord>::value << 'n'
        << "DWord is trivially copyable "
        << std::is_trivially_copyable<DWord>::value << 'n'
        << "Word is trivially copyable "
        << std::is_trivially_copyable<Word>::value << 'n'
        << "Byte is trivially copyable "
        << std::is_trivially_copyable<Byte>::value << 'n'
        //      << "Bits is trivially copyable "
        //<< std::is_trivially_copyable<Bits>::value << 'n'
        << "My Register is trivially copyable "
        << std::is_trivially_copyable<Register>::value << "nn";

    std::cout << "sizeof(std::bitset<Byte>) = "  << sizeof(Byte)  << " bytesn";
    std::cout << "sizeof(std::bitset<Word>) = "  << sizeof(Word)  << " bytesn";
    std::cout << "sizeof(std::bitset<DWord>) = " << sizeof(DWord) << " bytesn";
    std::cout << "sizeof(std::bitset<QWord>) = " << sizeof(QWord) << " bytesn";
    std::cout << "sizeof(Register) = "      << sizeof(Register) << " bytesnn";
    Register r;
    std::cout << "sizeof(Register::byte) = " << sizeof(r.byte)  << " bytesn";
    std::cout << "sizeof(Register::Byte) = " << sizeof(r.byte) / sizeof(r.byte[0]) << " bytesn";
    std::cout << "sizeof(Register::word) = " << sizeof(r.word)  << " bytesn";
    std::cout << "sizeof(Register::Word) = " << sizeof(r.word) / sizeof(r.word[0]) << " bytesn";
    std::cout << "sizeof(Register::dword) = " << sizeof(r.dword) << " bytesn";
    std::cout << "sizeof(Register::DWord) = " << sizeof(r.dword) / sizeof(r.dword[0]) << " bytesn";
    std::cout << "sizeof(Register::value) = " << sizeof(r.value) << " bytesn";
    std::cout << "sizeof(Register) = " << sizeof(r) << " bytesnn";
    r.value = 0xFFFFFFFFFFFFFFFF;
    std::cout << "value = " << r.value.to_ullong() << 'n' << r.value << 'n';
    for (std::uint16_t i = 0; i < 8; i++) {
        std::cout << "byte_" << i << " : " << r.byte[i] << 'n';
    }
    return EXIT_SUCCESS;
}

register.h

#pragma once
#include <algorithm>
#include <bitset>
#include <string>
#include <vector> // include for typedefs below.
namespace vpc {
    typedef std::int8_t  i8;
    typedef std::int16_t i16;
    typedef std::int32_t i32;
    typedef std::int64_t i64;
    const std::uint16_t BYTE = 0x08;
    const std::uint16_t WORD = 0x10;
    const std::uint16_t DWORD = 0x20;
    const std::uint16_t QWORD = 0x40;
    typedef std::bitset<BYTE>  Byte;
    typedef std::bitset<WORD>  Word;
    typedef std::bitset<DWORD> DWord;
    typedef std::bitset<QWORD> QWord;
    struct Register {
        union {
            QWord value{ 0 };
            union {
                DWord dword[2];
                struct {
                    DWord dword0;
                    DWord dword1;
                };
            };
            union {
                Word word[4];
                struct {
                    Word word0;
                    Word word1;
                    Word word2;
                    Word word3;
                };
            };
            union {
                Byte byte[8];
                struct {
                    Byte byte0;
                    Byte byte1;
                    Byte byte2;
                    Byte byte3;
                    Byte byte4;
                    Byte byte5;
                    Byte byte6;
                    Byte byte7;
                };
            };
        };
        Register() : value{ 0 } {}
    };
    Register reverseBitOrder(Register& reg, bool copy = false);
} // namespace vpc

register.cpp

#include "Register.h"
namespace vpc {
    Register reverseBitOrder(Register& reg, bool copy) {
        auto str = reg.value.to_string();
        std::reverse(str.begin(), str.end());
        if (copy) { // return a copy
            Register cpy;
            cpy.value = QWord(str);
            return cpy;
        }
        else {
            reg.value = QWord(str);
            return {};
        }
    }
} // namespace vpc

输出

std::bitset<64> is trivially copyable true
QWord is trivially copyable true
DWord is trivially copyable true
Word is trivially copyable true
Byte is trivially copyable true
My Register is trivially copyable true
sizeof(std::bitset<Byte>) = 4 bytes
sizeof(std::bitset<Word>) = 4 bytes
sizeof(std::bitset<DWord>) = 4 bytes
sizeof(std::bitset<QWord>) = 8 bytes
sizeof(Register) = 32 bytes
sizeof(Register::byte) = 16 bytes
sizeof(Register::Byte) = 4 bytes
sizeof(Register::word) = 16 bytes
sizeof(Register::Word) = 4 bytes
sizeof(Register::dword) = 8 bytes
sizeof(Register::DWord) = 2 bytes
sizeof(Register::value) = 8 bytes
sizeof(Register) = 32 bytes
value = 18446744073709551615
1111111111111111111111111111111111111111111111111111111111111111
byte_0 : 11111111
byte_1 : 11111111
byte_2 : 11001100
byte_3 : 11001100
byte_4 : 11001100
byte_5 : 11001100
byte_6 : 11001100
byte_7 : 11001100

查看了bitset类型的大小的打印数据后,然后将它们与它们作为结构内部的结构成员的实际尺寸进行比较。我试图弄清楚这里的引擎盖下发生了什么。

我不确定我是否正确地执行计算的大小计算,这是否与bitset的内部存储有关基础类型是std::bitset类型的结构。从标题中,您可以看到其中有4个变体:bitset<8> = Bytebitset<16> = Wordbitset<32> = DWord&amp;bitset<64> = QWord

本质中应该有这些可分配的映射:

// each [] = 1 byte or 8 bits for simplicity
bitset<64> = [] [] [] [] [] [] [] []
bitset<32> = [] [] [] []
bitset<16> = [] []
bitset<8>  = []

所以当我尝试在联合中使用它们时:

union {
    QWord q;
    union {
        DWord d[2];
        struct {
            DWord d_0;
            DWord d_1;
        };
    };
    union {
        Word w[4];
        struct {
            Word w_0;
            Word w_1;
            Word w_2;
            Word w_3;
         };
    };
    union {
        Byte b[8];
        struct {
            Byte b_0;
            Byte b_1;
            Byte b_2;
            Byte b_3;
            Byte b_4;
            Byte b_5;
            Byte b_6;
            Byte b_7; 
        };
    };
};

我认为,通过使用我在本联盟上方显示的模式,我将能够将数据包装到字节大小对齐中:

// each inner [] = 1 byte or 8 bits
// and each outer [] = index into array
         0   1   2   3   4   5   6   7
value = [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ]
                 0               1
dword[2] = [[] [] [] []],  [[] [] [] []]
             0        1        2        3
word[4] = [[] []], [[] []], [[] []], [[] []] 
             0     1     2     3     4     5     6     7
byte[8]  = [[ ]] [[ ]] [[ ]] [[ ]] [[ ]] [[ ]] [[ ]] [[ ]]

但是,这似乎并没有像我期望的那样发生。

我的总体目标是模拟我所表达的图案,以便寄存器的基本大小为64位或8个字节宽,通过使用工会,我将能够从完整的Qword。

您能详细说明我在这里缺少的东西吗?我不确定是否与std::bitset如何存储在内存中有关,是否与结构对齐有关,或者是否必须与工会本身打交道。

您想要做的事情不能以您想做的方式完成。std::bitset不能保证其尺寸,因此没有期望bitset<8>具有字节的大小。如果您不是活跃的联盟成员,您也没有任何方法可以访问这些bitset s的成员。

您想做的是:

  1. 存储uint64_t
  2. 通过与范围兼容的对象访问该 uint64_t位的各个子集,该对象允许您操纵它们。

因此,只需实现。您需要的不是bitset,而是Bit-range 查看类型,它允许您解释和操纵该uint64_t中的任何连续序列为范围。基本上,您需要bitset的接口,而是通过引用存储(以及该存储的特定范围(,而不是由为存储。您不存储这些范围;您应要求生成范围。

语言标准中没有什么可以指定bitset在内部处理存储的方式。我查看的一个实现使用了一个unsigned long数组来存储32个或更少的位(unsigned long long的32多个(。这可能是为了提高效率。

使用此存储方案,即使不全部使用,您的ByteWordDWord类型也将占四个字节。将它们的阵列存储在您的较大工会中会导致联盟的尺寸增长,因为每个bitset中都有未使用的字节。

为了消除这些未使用的字节,您必须使用其他内容。