在c ++中使用union时是否可以跳过字节?

时间:2018-08-22 08:20:41

标签: c++ arrays multidimensional-array unions

我正在尝试对两个结构使用联合,具体来说是将2D(4 x 4)浮点数组与4个Vector4对齐。

据我了解,union会从第一个字节开始查看顺序字节。如果我有一个4x4数组,并且想要整列,那么我需要跳过16个字节才能到达下一行。

示例:

Matrix = {{a, b, c, d},
          {e, f, g, h},
          {h, i, j, k},
          {l, m, n, o};

如果给出的数据使得一维数组为{a,b,c,d,e,..., o},我将如何获得最后一列(d,h,k,o)?

那么我将如何获取字节:12-> 16、28-> 32、44-> 48、60-> 64?

返回最后一行(不是我想要的)的代码是:

//matrix.h
struct Matrix4 {
    union{
        float values[4][4];
        Vector4 column[4];
    };

    //matrix methods etc
}

//main.cpp
Matrix4 position = Matrix3::translation(Vector3(1,2,3));
Vector4 column = position.column[2];
std::cout << (column) << std::endl;

输出矩阵为(抱歉,不记得如何使用LaTex格式):

| 1 0 0 1 |
| 0 1 0 2 |
| 0 0 1 3 |
| 0 0 0 1 |

所以我想要的列是:

|1|
|2|
|3|
|1|

联合给出:

|0|
|0|
|0|
|1|

(最后一行,而不是列)。

将代码重构为使用一维数组来获取顺序字节会更容易,还是应该更改矩阵的构造方式?或者我可以结合其他方式处理这个问题?

编辑:我的问题与此other question不同,因为我已经创建了一个矩阵类并分配了内存。我的问题着眼于“联合”的功能及其局限性,以及使用我定义的结构“ Vector4”作为表示矩阵中某些数据的一种方式。我正在研究跳过字节以接收彼此不直接跟随的数据的可能性,而不是如何分配所述字节。

4 个答案:

答案 0 :(得分:3)

这是一种相当常见的模式,但无效。之所以受欢迎,是因为它很容易,而且因为传统上主流编译器都允许并支持它。

但是,您根本无法以明确定义的方式以这种方式使用联合。联合不是将一种类型的数据重新解释为另一种类型的数据的一种方式。哎呀,reinterpret_cast甚至都无法到达您的位置:要安全地做到这一点,您必须保持std::copy来回移动字节。唯一安全的方法是使用char[](或unsigned char[]std::byte[]),该别名可以别名为任意内存块:因此,联合可用于快速字节检查。除此之外,仅当您一次不需要使用多个工会会员时,它们才有用。

您真正应该做的是选择一个 布局/格式,然后在顶部添加自己的operator[](或其他函数)变体,以提供不同的接口和解释数据的方式在您的呼叫站点。

做对了,不会有任何开销。

巧妙地执行此操作,便可以按照需要的方式“即时”应用转换。提供一种代理类型,一种迭代器,它不仅按线性顺序进行迭代,而且还按所需的修改顺序进行迭代。

此逻辑都可以封装为您的Matrix4类型。

答案 1 :(得分:2)

您不能同时访问一个联合的两个不同成员。 cppreference中的示例:

#include <string>
#include <vector>

union S
{
    std::string str;
    std::vector<int> vec;
    ~S() {} // needs to know which member is active, only possible in union-like class 
};          // the whole union occupies max(sizeof(string), sizeof(vector<int>))

int main()
{
    S s = {"Hello, world"};
    // at this point, reading from s.vec is undefined behavior
[...]

ie,即在分配了str成员后,您将无法访问vec成员。

答案 2 :(得分:1)

因此,我决定在此添加一个全新的答案,我认为它不涉及未定义的行为。主演力量类型转换:

#include <cstdlib>
#include <iostream>
#include <initializer_list>

using namespace std;

// Matrix4f dimension
#define DIM_MAT4        (4)

// Data structures
typedef struct _Vector4f
{
    float entries[DIM_MAT4];

    void
    println() const;

} Vector4f;

typedef struct _Matrix4f
{
    _Matrix4f(initializer_list<float> fl) : _trans_cache(NULL), _renew_cache(true)
    {
        copy(fl.begin(), fl.end(), entries);
    }

    _Matrix4f() : entries{.0f, .0f, .0f, .0f,
                    .0f, .0f, .0f, .0f,
                    .0f, .0f, .0f, .0f,
                    .0f, .0f, .0f, .0f}, _trans_cache(NULL), _renew_cache(true) {}

    ~_Matrix4f() { delete _trans_cache; }

    float &
    operator() (int, int);

    const float &
    operator() (int, int) const;

    const Vector4f *
    getRow(int) const;

    const Vector4f *
    getCol(int) const;

    _Matrix4f *
    transpose() const;

    void
    println() const;

private:

    float entries[DIM_MAT4 * DIM_MAT4];

    // cache the pointer to the transposed matrix
    mutable _Matrix4f *
    _trans_cache;
    mutable bool
    _renew_cache;

} Matrix4f;

Matrix4f *
Matrix4f::transpose() const
{
    if (not _renew_cache)
        return this->_trans_cache;

    Matrix4f * result = new Matrix4f;

    for (int k = 0; k < DIM_MAT4 * DIM_MAT4; k++)
    {
        int j = k % DIM_MAT4;
        int i = k / DIM_MAT4;

        result->entries[k] = this->entries[i + DIM_MAT4 * j];
    }

    _renew_cache = false;
    return this->_trans_cache = result;
}

float &
Matrix4f::operator() (int rid, int cid)
{
    _renew_cache = true;
    return this->entries[rid * DIM_MAT4 + cid];
}

const float &
Matrix4f::operator() (int rid, int cid) const
{
    return this->entries[rid * DIM_MAT4 + cid];
}

const Vector4f *
Matrix4f::getRow(int rid) const
{
    return reinterpret_cast<Vector4f *>(&(this->entries[rid * DIM_MAT4]));
}

const Vector4f *
Matrix4f::getCol(int cid) const
{
    return this->transpose()->getRow(cid);
}

void
Matrix4f::println() const
{
    this->getRow(0)->println();
    this->getRow(1)->println();
    this->getRow(2)->println();
    this->getRow(3)->println();

    cout << "Transposed: " << this->_trans_cache << endl;
}

void
Vector4f::println() const
{
    cout << '(' << (this->entries[0]) << ','
            << (this->entries[1]) << ','
            << (this->entries[2]) << ','
            << (this->entries[3]) << ')' << endl;
}

// Unit test
int main()
{
    Matrix4f mat = {1.0f, 0.0f, 0.0f, 1.0f,
                    0.0f, 1.0f, 0.0f, 2.0f,
                    0.0f, 0.0f, 1.0f, 3.0f,
                    0.0f, 0.0f, 0.0f, 1.0f};

    mat.println();

    // The column of the current matrix
    // is the row of the transposed matrix
    Vector4f * col = mat.getCol(3);

    cout << "Vector:" << endl;

    col->println();

    cout << "Matrix(2,3) = " << mat(2, 3) << endl;

    return 0;
}

答案 3 :(得分:1)

联合仅用于为不同对象保留一个内存区域。但是在每一瞬间,只有一个活着的物体。因此,通常来说,如果您有一个由两个成员ab组成的联合,则如果您初始化了a并且a是联合的活动成员,则应该永远不要尝试读取b的值。

但是在ab共享一个共同的初始序列的情况下,该规则有一个例外:在这种情况下,标准确保了a和{{ 1}}非常相似,因此您可以通过b访问a的值。

下面的代码示例显示通过非活动成员访问联合值的已定义行为:

b