Экспериментируя с Unions и Bitfields в структурах и шаблонах

Чтобы лучше понять и понять, как работают битовые поля, объединения и байтовое выравнивание структур, я моделирую структуру регистра шаблона.

Требования моего Регистра следующие:

  • Размер или ширина регистра по умолчанию составляет 8 бит или 1 байт.
  • Регистры большего размера должны быть кратны 8
  • Регистры имеют размер не более 64 бит или 8 байтов.

У меня есть набор структур, которые строятся друг от друга в каскадном эффекте, начиная с базовой единицы байта и заканчивая QWord.

Мои регистры являются шаблонными специализациями.

Вот мой код до сих пор:

-main.cpp-

#include <iostream>
#include "Register.h"

int main() {
    Register r1;
    r1.value.value_ = 8;

    Register<16> r2;
    r2.value.value_ = 16;

    Register<32> r3;
    r3.value.value_ = 32;

    Register<64> r4;
    r4.value.value_ = 64;

    std::cout << static_cast<std::uint8_t>( r1.value.value_) << "\n";
    std::cout << static_cast<std::uint16_t>(r2.value.value_) << "\n";
    std::cout << static_cast<std::uint32_t>(r3.value.value_) << "\n";
    std::cout << static_cast<std::uint64_t>(r4.value.value_) << "\n";

    return EXIT_SUCCESS;
}

-Register.h-

#pragma once

#include <vector> // include for typedefs below.
typedef std::int8_t  i8;
typedef std::int16_t i16;
typedef std::int32_t i32;
typedef std::int64_t i64;

struct MyByte {
    union {
        i8 value_;
        struct {
            i8 b0 : 1;
            i8 b1 : 1;
            i8 b2 : 1;
            i8 b3 : 1;
            i8 b4 : 1;
            i8 b5 : 1;
            i8 b6 : 1;
            i8 b7 : 1;
        };
    };
};

struct MyWord {        // same as short or i16  
    union {
        i16 value_;
        union {
            MyByte byte_[2];
            struct {
                MyByte b0_;
                MyByte b1_;
            };
        };
    };
};

struct MyDWord {       // same as int or i32
    union {
        i32 value_;

        struct {
            MyWord w0_;
            MyWord w1_;
        };

        union {
            MyByte byte_[4];
            struct {
                MyByte b0_;
                MyByte b1_;
                MyByte b2_;
                MyByte b3_;
            };
        };
    };
};

struct MyQWord {     // same as long or i64
    union {
        i64 value_;
        struct {
            MyDWord d0_;
            MyDWord d1_;
        };
        struct {
            MyWord w0_;
            MyWord w1_;
            MyWord w2_;
            MyWord w3_;
        };
        union { 
            MyByte byte_[8];
            struct {
                MyByte b0_;
                MyByte b1_;
                MyByte b2_;
                MyByte b3_;
                MyByte b4_;
                MyByte b5_;
                MyByte b6_;
                MyByte b7_;
            };
        };
    };
};

template<size_t N = 8>
struct Register {
    MyByte value;
    Register() {
        static_assert(
         ((N % 8) == 0) &&
         (N >= 8) &&
         (N <= 64)

        );
    }
};

template<>
struct Register<16> {
    MyWord value;
    Register() = default;
};

template<>
struct Register<32> {
    MyDWord value;
    Register() = default;
};

template<>
struct Register<64> {
    MyQWord value;
    Register() = default;
};

Приведенный выше код компилируется, запускается и завершается с кодом 0 в Visual Studio 2017 с компилятором, настроенным на последний набросок стандарта.

Теперь, когда вы увидели код, я до некоторой степени понимаю битовые поля и союзы, но когда я не использую их все так часто, они могут меня немного сбить с толку. Я знаю, что при их использовании, особенно в сочетании, это может привести к тому, что код не будет переносимым, особенно в различных компиляторах, операционных системах и архитектурах (endian).

Тем не менее, это всего лишь экспериментальный код, чтобы практиковаться в них как в хорошем переподготовке.

Проблема, с которой я столкнулся здесь, связана с моим выводом. Все регистры более высокого порядка, кажется, работают нормально, я только протестировал получение его непосредственно через внутреннюю переменную-член value_ напрямую. Тем не менее, я получаю результаты из стандартного или самого основного регистра 8-битного размера. Со значениями, установленными как они есть в настоящее время. Я получаю это в качестве вывода:

--

16
32
64

И если я изменю свой основной на это:

#include <iostream>
#include "Register.h"

int main() {
    Register r;
    for (i8 i = 0; i < 21; i++) {
        1.value.value_ = i;
        std::cout << static_cast<std::uint8_t>(r.value.value_) << "\n";
    }
    return EXIT_SUCCESS;
}

Я получаю этот вывод со звуковым сигналом, где-то в смеси этого:

☺
☻
♥
♦
♣
♠





♂
♀

♫
☼
►
◄
↕
‼
¶

Это как-то связано с тем, как std::int8_t определено? Это основано на char тип значения вместо int тип? Это все еще должно быть целым, хотя... если это так, то каким образом он должен иметь дело со значениями без знака в объединениях или битовых полях и т. Д.? Что вызывает символы ASCII для печати на консоль.

1 ответ

Решение

Это основано на типе значения типа char вместо типа int?

char * является * целочисленным типом. Стандарт позволяет std::int8_t быть typedef для char,

std::cout << static_cast<std::uint64_t>(r.value.value_) << "\n";

FTW.

Другие вопросы по тегам