首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用c#的StrucLayout和FieldOffset表示联合位域

使用c#的StrucLayout和FieldOffset表示联合位域
EN

Stack Overflow用户
提问于 2011-01-29 01:05:37
回答 3查看 2.5K关注 0票数 5

我知道为了在C#中表示联合,我需要使用StructLayoutLayoutKind.Explicit)和FieldOffset(x)属性来指定联合中的字节偏移量。但是,我有一个以下联合,我想表示和FieldOffset属性只偏移一个字节的大小。

代码语言:javascript
复制
union _myUnion
{
     unsigned int info;
     struct
     {
          unsigned int flag1:1 // bit 0
          unsigned int flag2:1 // bit 1
          unsigned int flag3:1 // bit 2
          unsigned int flag4:1 // bit 3
          unsigned int flag5:1 // bit 4
          unsigned int flag6:1 // bit 5
          .
          .
          .
          unsigned int flag31:1 // bit 31
     }
}

正如您在联合中看到的内部结构一样,我不能使用FieldOffset,因为我需要一些可以稍微偏移的东西。

对此有解决方案吗?我正在尝试调用一个DLL函数,其中一个数据结构就是这样定义的,我没有办法最好地表示这个联合结构。

EN

回答 3

Stack Overflow用户

发布于 2011-01-29 01:15:38

那里不需要联合;一个用于数据的field+property,8个执行按位“移位”操作的属性,例如:

代码语言:javascript
复制
public uint Value {get;set;}

public uint Flag2 {
   get { return Value >> 2; }
}

等等。我还以为你想要bool在这里?

通常我会说:不要做可变的结构。PInvoke可能(我不确定)是一个有效的场景,所以我将忽略它:)

如果该值确实使用了超过32位的值,请考虑将支持字段切换为ulong。

票数 4
EN

Stack Overflow用户

发布于 2018-11-24 14:30:06

是的,你可以做到。您走在正确的道路上,答案在于使用BitVector32以及FieldOffsetStructLayout属性。但是,在执行此操作时,您需要记住以下几点:

  1. 您需要显式地指定将包含相关数据的变量的大小。这第一个项目是非常重要的要注意。例如,在上面的问题中,将info指定为unsigned int无符号整数的大小是多少? 32位? 64位?这取决于运行此特定版本.NET的操作系统的版本(这可能是.NET核心、单声道或Win32/Win64).
  2. What 'endianness‘或位顺序?同样,我们可能在任何类型的硬件上运行(考虑移动/Xamarin,而不仅仅是笔记本电脑或平板电脑) --因此,您不能假定英特尔的位顺序。
  3. 我们将希望避免任何依赖于语言或C/C++术语POD (普通旧式数据)类型的内存管理。这意味着只使用值类型。

我将根据您的问题和标志0-31的说明,假设整数大小为32。

那么诀窍就是确保以下几点:

  1. 所有数据都是字节对齐的。
  2. 位字段和info字段在同一字节边界上对齐。

下面是我们实现这一点的方法:

代码语言:javascript
复制
[StructLayout(LayoutKind.Explicit, Size = 1, CharSet = CharSet.Ansi)]
public struct MyUnion
{
    #region Lifetime

    /// <summary>
    /// Ctor
    /// </summary>
    /// <param name="foo"></param>
    public MyUnion(int foo)
    {
        // allocate the bitfield
        info = new BitVector32(0);

        // initialize bitfield sections
        flag1 = BitVector32.CreateSection(1);
        flag2 = BitVector32.CreateSection(1, flag1);
        flag3 = BitVector32.CreateSection(1, flag2);
    }

    #endregion

    #region Bifield

    // Creates and initializes a BitVector32.
    [FieldOffset(0)]
    private BitVector32 info;

    #endregion

    #region Bitfield sections

    /// <summary>
    /// Section - Flag 1
    /// </summary>
    private static BitVector32.Section flag1;

    /// <summary>
    /// Section - Flag 2
    /// </summary>
    private static BitVector32.Section flag2;

    /// <summary>
    /// Section - Flag 3
    /// </summary>
    private static BitVector32.Section flag3;

    #endregion

    #region Properties

    /// <summary>
    /// Flag 1
    /// </summary>
    public bool Flag1
    {
        get { return info[flag1] != 0; }
        set { info[flag1] = value ? 1 : 0; }
    }

    /// <summary>
    /// Flag 2
    /// </summary>
    public bool Flag2
    {
        get { return info[flag2] != 0; }
        set { info[flag2] = value ? 1 : 0; }
    }

    /// <summary>
    /// Flag 1
    /// </summary>
    public bool Flag3
    {
        get { return info[flag3] != 0; }
        set { info[flag3] = value ? 1 : 0; }
    }

    #endregion

    #region ToString

    /// <summary>
    /// Allows us to represent this in human readable form
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        return $"Name: {nameof(MyUnion)}{Environment.NewLine}Flag1: {Flag1}: Flag2: {Flag2} Flag3: {Flag3}  {Environment.NewLine}BitVector32: {info}{Environment.NewLine}";
    }

    #endregion
}

特别要注意构造函数。根据定义,不能为C#中的结构定义默认构造函数。但是,我们需要一些方法来确保BitVector32对象及其部分在使用前已正确初始化。我们需要一个接受伪整数参数的构造函数,并像这样初始化对象:

代码语言:javascript
复制
    /// <summary>
    /// Main entry point
    /// </summary>
    /// <param name="args"></param>
    static void Main(string[] args)
    {
        // brew up one of these...
        var myUnion = new MyUnion(0)
        {
            Flag2 = true
        };

顺便说一句,您绝不会局限于单个位域--您可以定义任意大小的位域。例如,如果我将您的示例更改为:

代码语言:javascript
复制
union _myUnion
{
    unsigned int info;
    struct
    {
        unsigned int flag1 : 3 // bit 0-2
        unsigned int flag2 : 1 // bit 3
        unsigned int flag3 : 4 // bit 4-7
            .
            .
            .
        unsigned int flag31 : 1 // bit 31
    }
}

我只需将我的类改为:

代码语言:javascript
复制
[StructLayout(LayoutKind.Explicit, Size = 1, CharSet = CharSet.Ansi)]
public struct MyUnion2
{
    #region Lifetime

    /// <summary>
    /// Ctor
    /// </summary>
    /// <param name="foo"></param>
    public MyUnion2(int foo)
    {
        // allocate the bitfield
        info = new BitVector32(0);

        // initialize bitfield sections
        flag1 = BitVector32.CreateSection(0x07);
        flag2 = BitVector32.CreateSection(1, flag1);
        flag3 = BitVector32.CreateSection(0x0f, flag2);
    }

    #endregion

    #region Bifield

    // Creates and initializes a BitVector32.
    [FieldOffset(0)]
    private BitVector32 info;

    #endregion

    #region Bitfield sections

    /// <summary>
    /// Section - Flag1
    /// </summary>
    private static BitVector32.Section flag1;

    /// <summary>
    /// Section - Flag2
    /// </summary>
    private static BitVector32.Section flag2;

    /// <summary>
    /// Section - Flag3
    /// </summary>
    private static BitVector32.Section flag3;

    #endregion

    #region Properties

    /// <summary>
    /// Flag 1
    /// </summary>
    public int Flag1
    {
        get { return info[flag1]; }
        set { info[flag1] = value; }
    }

    /// <summary>
    /// Flag 2
    /// </summary>
    public bool Flag2
    {
        get { return info[flag2] != 0; }
        set { info[flag2] = value ? 1 : 0; }
    }

    /// <summary>
    /// Flag 1
    /// </summary>
    public int Flag3
    {
        get { return info[flag3]; }
        set { info[flag3] = value; }
    }

    #endregion

    #region ToString

    /// <summary>
    /// Allows us to represent this in human readable form
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        return $"Name: {nameof(MyUnion2)}{Environment.NewLine}Flag1: {Flag1}: Flag2: {Flag2} Flag3: {Flag3}  {Environment.NewLine}BitVector32: {info}{Environment.NewLine}";
    }

    #endregion
}

关于这个话题的最后一句话。这应该是显而易见的,只有当你绝对做这件事的时候才应该这样做。显然,这需要您的操作系统环境、您运行的语言、调用约定以及许多其他脆弱需求的专业知识。

在任何其他上下文中,这里都有太多的代码味道,这显然是不可移植性的尖叫。但从你的问题的背景来看,我猜测整个问题的要点是,你需要接近硬件并需要这种精确度。

买者自负!

票数 1
EN

Stack Overflow用户

发布于 2018-11-25 03:01:16

最好的解决方案是使用标志枚举

最好使用uint而不是int

代码语言:javascript
复制
[Flags]
public enum MyEnum
    : uint
{
    None=0,
    Flag1=1,
    Flag2=1<<1,
    Flag3=1<<2,
    // etc
    Flag32=1<<31
}

之后,您可以使用int作为枚举和uint

代码语言:javascript
复制
MyEnum value=MyEnum.Flag1|MyEnum.Flag2;
uint uintValue=(uint)value;
// uintValue=3

这通常由PInvoke进行封送处理

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4830978

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档