将一个结构放入比它小的内存中

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

我正在优化一种压缩算法,该算法使用跨 2 个字节的结构。但有时我希望它只解释 1 个字节,因为(我希望)映射到第二个字节的成员永远不会被写入或读取。

我是否能保证只要

zFmt
wFmt
从未被访问过,编译器就不会访问第二个字节?如果不是,我可以编写一个静态断言,当这个假设错误时将停止编译吗?

struct Header {
    uint8_t xFmt : 4;
    uint8_t yFmt : 4;
    uint8_t zFmt : 4; // must not be read/written when header is mapped to 1 byte
    uint8_t wFmt : 4; // must not be read/written when header is mapped to 1 byte
};
    
static_assert( sizeof(Header) == 2 && alignof(Header) == 1, "alignment vital");


// --- usage ---
int main(){
    // Header may be placed into memory where it overlaps only one byte;
    // in that case, it's .zFmt and .wFmt members are never read or written to
    char buffer[1];
    Header * header = new (buffer) Header;

    // can I be sure (or statically assert) that these instructions
    // will only read and write to the nearest (and only) owned byte?
    header->xFmt = 0;
    header->yFmt = 0;
    header->xFmt += 1; 
    header->yFmt += 1; 
}

旁注:

该算法目前有效,但我想确保它不依赖于未定义的行为。我相信通过使用新的放置来遵守严格别名,但也许这个假设是不正确的?

另外,我想以这种方式使用这个结构和位字段......因为它们看起来不错!这不是最好的原因哈哈,所以如果这是不可能的,我的后备方法是解释没有结构的字节,如带有移位和掩码的

uint8_t
。我还知道我可以使用继承来执行切片,如果这是未定义的,我将研究它。

c++ optimization language-lawyer bit-fields strict-aliasing
1个回答
0
投票

回答我自己的问题,因为我相信我已经在 2020 版 C++ ISO 标准中找到了答案(将相关部分加粗):

  • 内存位置可以是标量类型的对象,也可以是相邻位域的最大序列,所有位域都具有 非零宽度。 [注意:语言的各种功能,例如引用和虚函数,可能涉及 程序无法访问但由实现管理的附加内存位置。 — 尾注] 两个或多个执行线程 (6.9.2) 可以访问单独的内存位置而不会干扰 与彼此。
  • [注:因此,位字段和相邻的非位字段位于不同的存储位置,因此可以 由两个执行线程同时更新,互不干扰。 这同样适用于两个位域, 如果一个在嵌套结构声明内声明而另一个不在嵌套结构声明内,或者两者由 零长度位域声明,或者它们被非位域声明分隔。 这是不安全的 如果它们之间的所有字段也是非零位字段,则同时更新同一结构中的两个位字段 宽度。 — 尾注]
struct {
    char a;
    int b:5,
    c:11,
    :0,
    d:8;
    struct {int ee:8;} e;
}
  • [示例:声明为[上述]的类包含四个单独的内存位置:成员 a 和位域 d 和 e.ee 都是单独的内存 位置,并且可以同时修改而不会互相干扰。位域 b 和 c 一起构成第四个存储位置。位域 b 和 c 不能同时修改,但是 例如,b和a可以是。 —结束示例]

关于“两个或多个执行线程”访问单独的内存位置的注释很重要,因为它确保访问一个“内存位置”的代码无法加载或写入另一内存位置。 因此,在我的代码中访问

yFmt
无法触及
zFmt
并导致访问冲突(如果该内存不属于该对象)。结合结构的所有成员和位字段必须具有递增地址的规则,我相信这使我的用法定义明确,并进行了以下更改:

struct Header {
    uint8_t xFmt : 4;
    uint8_t yFmt : 4;
    uint8_t :0;
    uint8_t zFmt : 4; // must not be read/written when header is mapped to 1 byte
    uint8_t wFmt : 4; // must not be read/written when header is mapped to 1 byte
};

static_assert( sizeof(Header) == 2 && alignof(Header) == 1, "");

当然,并不是说它不容易导致程序员错误,但它似乎确实定义良好。

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