如何在 Go 中访问 C 位域

abe*_*ier 5 go cgo

我有一个像这样的结构:

typedef struct st_MASK_SETTINGS
{
  uint32_t foo  : 1;
  uint32_t bar  : 7;
} MASK_SETTINGS
Run Code Online (Sandbox Code Playgroud)

现在我想通过 cgo 访问foo- 但找不到任何文档如何做到这一点。

天真的v := ms.foo抱怨has no field or method

Dan*_*tos 1

好吧,你不会喜欢这个答案,但是

  1. 没有可移植的方法来做到这一点,因为位域打包在 C 和 C++ 中都是“实现定义的”,并且
  2. Go 中的位字段支持似乎相当糟糕(可能是由于#1)。

首先,位域的布局在每个现有的 C 和 C++ 标准中都是实现定义的。这意味着没有一个标准指定位域定义中的位应该如何打包(即它们应该去哪里)——这完全取决于编译器。在给定一些编译器示例的情况下,您可能会发现它们在实践中如何以某种方式布局,但您将深入到未定义的行为领域。

我们正在 gcc 中的bug #83784下解决这个问题(我所说的“我们”是指 Andrew Pinski),我希望在 gcc 10 或 11 中我们能找到一个最佳解决方案。需要明确的是,现在有一个解决方案 - 它使用联合并定义打包和解包函数来读取每个位字段并手动将数据放置在内存中所属的位置。问题是,当您正确猜测 gcc 使用的位布局时,该函数应该变为无操作并“编译离开”。目前这种情况还没有发生。

例子:

union a {
    struct {
        int field1:12;
        int field2:20;
    };
    int packed;
};

static union a a_pack(union a a)
{
    union a ret = {0};

    ret.packed = (a.field1 & ((1 << 12) - 1) << 20;
    ret.packed |= a.field2 & ((1 << 20) - 1)

    return ret;
}

static union a a_unpack(union a a)
{
    union a ret = {0};

    ret.field1 = a.packed >> 20;
    ret.field2 = a.packed & ((1 << 20) - 1);

    return ret;
}
Run Code Online (Sandbox Code Playgroud)

一旦你这样做了,你就可以“打包”你的位域,从 Go 读取 a.packed,然后对其进行位修改或使用位域实现之一

我告诉过你你不会喜欢这个答案。:)