如何知道使用了 Union 中的哪个变量?

发布于 2024-10-02 22:15:01 字数 523 浏览 2 评论 0原文

如果我将 Union 声明为:

union TestUnion
{
    struct 
    {
      unsigned int Num;
      unsigned char Name[5];
    }TestStruct;
    unsigned char Total[7];
};

现在,我如何知道是使用 Total[7] 还是使用 TestStruct?

我正在使用C! 我正在重新审视工会和结构,这个问题出现在我的脑海中。 不能使用“sizeof”,因为两者的大小相同,即 7 字节。 (这里又出现了另一个问题)

当我只用字符“a”填充“Total”并尝试 sizeof(TestUnionInstance) 时,它返回 12(Char 的大小是 1 个字节,对吧?)。所以我从中分离出结构体,发现结构体的大小是12字节而不是5+2=7字节....奇怪! 有谁能解释一下吗??

PS 我使用的是 Visual Studio 2008。

If I declare a Union as:

union TestUnion
{
    struct 
    {
      unsigned int Num;
      unsigned char Name[5];
    }TestStruct;
    unsigned char Total[7];
};

Now, How can I know that whether Total[7] is used or TestStruct is used?

I am using C!
I was revisiting unions and structures and this question came to my mind.
"sizeof" can't be used as both are of same size i.e. 7 bytes. (And Here comes another question)

When I filled only "Total" with a Character 'a' and Tried sizeof(TestUnionInstance), it returned 12 (Size of Char is 1 byte, Right?). So I isolated the structure from it and found that Size of Structure is 12 bytes not 5+2=7 bytes.... Strange!!
Anybody can explain??

P.S. I am using Visual Studio 2008.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

怕倦 2024-10-09 22:15:01

你不能。这是工会的部分意义所在。

如果您需要能够分辨,您可以使用称为标记联合的东西。有些语言内置了对这些的支持,但在 C 中,你必须自己做。这个想法是在联合中包含一个标签,您可以使用它来判断它是哪个版本。就像:

enum TestUnionTag {NUM_NAME, TOTAL};

struct {
    enum TestUnionTag tag;
    union {
        struct {
            unsigned int Num;
            unsigned char Name[5];
        } TestStruct;
        unsigned char Total[7];
    } value;
} TestUnion;

然后在您的代码中,您确保始终设置标签来说明联合的使用方式。

关于 sizeof:结构体是 12 个字节,因为 int 有 4 个字节(大多数现代编译器有一个 4 字节 int,与 long int 相同),然后是 3 个字节的填充和 5 个字节的字符(我不知道)不知道填充是在字符之前还是之后)。填充的存在使得结构体具有整数个单词长,以便内存中的所有内容都在单词边界上保持对齐。因为结构体有 12 个字节长,所以联合体也必须有 12 个字节长才能容纳它;联合不会根据其中的内容改变大小。

You can't. That's part of the point of unions.

If you need to be able to tell, you can use something called a tagged union. Some languages have built-in support for these, but in C, you have to do it yourself. The idea is to include a tag along with the union which you can use to tell which version it is. Like:

enum TestUnionTag {NUM_NAME, TOTAL};

struct {
    enum TestUnionTag tag;
    union {
        struct {
            unsigned int Num;
            unsigned char Name[5];
        } TestStruct;
        unsigned char Total[7];
    } value;
} TestUnion;

Then in your code, you make sure you always set the tag to say how the union is being used.

About the sizeof: the struct is 12 bytes because there are 4 bytes for the int (most modern compilers have a 4-byte int, the same as a long int), then three bytes of padding and five bytes for the chars (i don't know if the padding comes before or after the chars). The padding is there so that the struct is a whole number of words long, so that everything in memory stays aligned on word boundaries. Because the struct is 12 bytes long, the union has to be 12 bytes long to hold it; the union doesn't change size according to what's in it.

万水千山粽是情ミ 2024-10-09 22:15:01

要使用的成员是您最后写信给的成员;其他人是禁区。你知道你最后一次写信给哪位成员,不是吗?毕竟,是您编写了该程序:-)


至于您的第二个问题:允许编译器在结构中插入“填充字节”以避免未对齐的访问并使其性能更高。

example of a possible distribution of bytes inside your structure

Num    |Name     |pad
- - - -|- - - - -|x x x
0 1 2 3|4 5 6 7 8|9 a b

The member to use is the one you last wrote to; the other(s) are off limits. You know which member you last wrote to, don't you? After all, it was you who wrote the program :-)


As for you secondary question: the compiler is allowed to insert 'padding bytes' in the structure to avoid unaligned accesses and make it more performant.

example of a possible distribution of bytes inside your structure

Num    |Name     |pad
- - - -|- - - - -|x x x
0 1 2 3|4 5 6 7 8|9 a b
×眷恋的温暖 2024-10-09 22:15:01

简短的回答:除了在联合体之外的结构中的某个位置添加枚举之外,没有其他办法。

enum TestUnionPart
{
  TUP_STRUCT,
  TUP_TOTAL
};

struct TestUnionStruct
{
  enum TestUnionPart Part;
  union
  {
    struct
    {
      unsigned int Num;
      unsigned char Name[5];
    } TestStruct;
    unsigned char Total[7];
  } TestUnion;
};

现在,您需要控制联合的创建,以确保正确设置枚举,例如使用类似于以下的功能:

void init_with_struct(struct TestUnionStruct* tus, struct TestStruct const * ts)
{
  tus->Part = TUP_STRUCT;
  memcpy(&tus->TestUnion.TestStruct, ts, sizeof(*ts));
}

正确值的分派现在是单个开关:

void print(struct TestUnionStruct const * tus)
{
  switch (tus->Part)
  {
    case TUP_STRUCT:
      printf("Num = %u, Name = %s\n",
             tus->TestUnion.TestStruct.Num,
             tus->TestUnion.TestStruct.Name);
      break;
    case TUP_TOTAL:
      printf("Total = %s\n", tus->TestUnion.Total);
      break;
    default:
      /* Compiler can't make sure you'll never reach this case */
      assert(0);
  }
}

作为旁注,我想提一下这些构造最好用 ML 系列语言来处理。

type test_struct = { num: int; name: string }
type test_union = Struct of test_struct | Total of string

Short answer: there is no way except by adding an enum somewhere in your struct outside the union.

enum TestUnionPart
{
  TUP_STRUCT,
  TUP_TOTAL
};

struct TestUnionStruct
{
  enum TestUnionPart Part;
  union
  {
    struct
    {
      unsigned int Num;
      unsigned char Name[5];
    } TestStruct;
    unsigned char Total[7];
  } TestUnion;
};

Now you'll need to control creation of your union to make sure the enum is correctly set, for example with functions similar to:

void init_with_struct(struct TestUnionStruct* tus, struct TestStruct const * ts)
{
  tus->Part = TUP_STRUCT;
  memcpy(&tus->TestUnion.TestStruct, ts, sizeof(*ts));
}

Dispatch on the correct values is now a single switch:

void print(struct TestUnionStruct const * tus)
{
  switch (tus->Part)
  {
    case TUP_STRUCT:
      printf("Num = %u, Name = %s\n",
             tus->TestUnion.TestStruct.Num,
             tus->TestUnion.TestStruct.Name);
      break;
    case TUP_TOTAL:
      printf("Total = %s\n", tus->TestUnion.Total);
      break;
    default:
      /* Compiler can't make sure you'll never reach this case */
      assert(0);
  }
}

As a side note, I'd like to mention that these constructs are best handled in languages of the ML family.

type test_struct = { num: int; name: string }
type test_union = Struct of test_struct | Total of string
俏︾媚 2024-10-09 22:15:01

首先,现在大多数架构上的 sizeof(int) 将为 4。如果您想要 2,您应该查看 shortint16_t如果您想具体的话,请参阅 C99 中的 stdint.h 标头。

其次,C 使用填充字节来确保每个struct 与字边界 (4) 对齐。所以你的结构看起来像这样:

+---+---+---+---+---+---+---+---+---+---+---+---+
|      Num      |   N   a   m   e   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+---+---+

末尾有 3 个字节。否则,数组中的下一个structNum 字段将位于一个难以对齐的位置,这会降低访问效率。

第三,联合的 sizeof 将是其最大成员的 sizeof。即使没有使用所有空间,sizeof 也将返回最大的结果。

正如其他答案所提到的,您需要某种其他方式(例如 enum)来确定使用联合的哪个字段。

First, sizeof(int) on most architectures nowadays is going to be 4. If you want 2 you should look at short, or int16_t in the stdint.h header in C99 if you want to be specific.

Second, C uses padding bytes to make sure each struct is aligned to a word-boundary (4). So your struct looks like this:

+---+---+---+---+---+---+---+---+---+---+---+---+
|      Num      |   N   a   m   e   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+---+---+

There's 3 bytes at the end. Otherwise, the next struct in an array would have it's Num field in an awkwardly-aligned place, which would make it less efficient to access.

Third, the sizeof a union is going to be the sizeof it's largest member. Even if all that space isn't used, sizeof is going to return the largest result.

You need, as other answers have mentioned, some other way (like an enum) to determine which field of your union is used.

庆幸我还是我 2024-10-09 22:15:01

没有办法说清楚。您应该有一些额外的标志(或联合体外部的其他方式)来说明真正使用了联合体的哪个部分。

There is no way to tell. You should have some additional flags (or other means external to your union) saying which of the union parts is really used.

好听的两个字的网名 2024-10-09 22:15:01

包含与枚举的联合以确定存储内容的另一个示例。我发现它更加清晰、切题。

从:
https://www.cs.uic.edu/~jbell/ CourseNotes/C_Programming/Structures.html

作者:
John T. Bell 博士


为了了解实际存储的是哪个联合字段,联合通常嵌套在结构内部,并用枚举类型指示实际存储的内容。例如:

typedef struct Flight {
    enum { PASSENGER, CARGO } type;
    union {
        int npassengers;
        double tonnages;  // Units are not necessarily tons.
    } cargo;
} Flight;

Flight flights[ 1000 ];

flights[ 42 ].type = PASSENGER;
flights[ 42 ].cargo.npassengers = 150;

flights[ 20 ].type = CARGO;
flights[ 20 ].cargo.tonnages = 356.78;

Another example of including the union with an enum to determine what is stored. I found it much more clear and to the point.

from:
https://www.cs.uic.edu/~jbell/CourseNotes/C_Programming/Structures.html

author:
Dr. John T. Bell


In order to know which union field is actually stored, unions are often nested inside of structs, with an enumerated type indicating what is actually stored there. For example:

typedef struct Flight {
    enum { PASSENGER, CARGO } type;
    union {
        int npassengers;
        double tonnages;  // Units are not necessarily tons.
    } cargo;
} Flight;

Flight flights[ 1000 ];

flights[ 42 ].type = PASSENGER;
flights[ 42 ].cargo.npassengers = 150;

flights[ 20 ].type = CARGO;
flights[ 20 ].cargo.tonnages = 356.78;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文