繁体   English   中英

C中结合的结构

[英]Structure within union in C

当变量与union相关联时,编译器会通过考虑最大内存的大小来分配内存。 因此,union的大小等于最大成员的大小。 所以这意味着改变任何成员的值将改变其他成员的价值。 但是当我执行以下代码时

output: 4 5 7.000000
union job
{
    int a;
    struct data
    {
        double b;
        int x
    }q;
} w;


w.q.b=7;
w.a=4;
w.q.x=5;

printf("%d %d %f",w.a,w.q.x,w.q.b);
return 0;
}

问题是,首先我分配a的值然后修改qx的值,然后a的值将被qx覆盖但是在输出中它仍然显示a的原始值以及qx的原始值我不能了解它为什么会发生?

你的理解是正确的 - 数字应该改变。 我拿了你的代码,并添加了一些,以向你展示究竟发生了什么。

真正的问题非常有趣,并且与浮点数在内存中的表示方式有关。

首先,让我们创建一个结构中使用的字节的映射:

aaaa
bbbbbbbbxxxx

正如你所看到的,前四个字节b与重叠的a 这将变得很重要。

现在我们来看看double通常存储的方式(我是从具有64位英特尔架构的Mac的角度来编写的。事实上,内存中的格式确实是IEEE754格式):

在此输入图像描述

这里需要注意的重要一点是,英特尔机器是“小端” - 也就是说,首先存储的数字是“右边的东西”,即“分数”的最低有效位。

现在让我们看一个与你的代码做同样事情的程序 - 但打印出结构的内容,以便我们看到发生了什么:

#include <stdio.h>
#include <string.h>

void dumpBytes(void *p, int n) {
  int ii;
  char hex[9];
  for(ii = 0; ii < n; ii++) {
    sprintf(hex, "%02x", (char)*((char*)p + ii));
    printf("%s ", hex + strlen(hex)-2);
  }
  printf("\n");
}

int main(void) {
static union job
{
    int a;
    struct data
    {
        double b;
        int x;
    }q;
} w;


printf("intial value:\n");
dumpBytes(&w, sizeof(w));
w.q.b=7;
printf("setting w.q.b = 7:\n");
dumpBytes(&w, sizeof(w));
w.a=4;
printf("setting w.a = 4:\n");
dumpBytes(&w, sizeof(w));
w.q.x=5;
printf("setting w.q.x = 5:\n");
dumpBytes(&w, sizeof(w));

printf("values are now %d %d %.15lf\n",w.a,w.q.x,w.q.b);
w.q.b=7;
printf("setting w.q.b = 7:\n");
dumpBytes(&w, sizeof(w));
printf("values are now %d %d %.15lf\n",w.a,w.q.x,w.q.b);
return 0;
}

并输出:

intial value:
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

全零(我声明变量static - 确保所有内容都被初始化)。 请注意,该函数打印出16个字节,即使您可能认为最大元素为double + int应该只有12个字节长。 这与字节对齐有关 - 当最大元素长度为8个字节时,结构将在8位边界上对齐。

setting w.q.b = 7:
00 00 00 00 00 00 1c 40 00 00 00 00 00 00 00 00 

让我们看一下以正确顺序表示double的字节:

40 1c 00 00 00 00 00 00

Sign bit = 0
exponent = 1 0000 0000 0111b  (binary representation)
mantissa = 0

setting w.a = 4:
04 00 00 00 00 00 1c 40 00 00 00 00 00 00 00 00 

当我们现在写a ,我们修改了第一个字节。 这对应于尾数的最低有效位,现在是(十六进制):

00 00 00 00 00 00 04

现在,尾数的格式意味着这个数字的左边是1 ; 因此,将最后一位从0更改为4会将数字的大小改变一小部分 - 您需要查看第15个小数才能看到它。

setting w.q.x = 5:
04 00 00 00 00 00 1c 40 05 00 00 00 00 00 00 00 

5写在自己的小空间中

values are now 4 5 7.000000000000004

注意 - 当我使用大量数字时,您可以看到b的最低有效部分不完全是7 - 即使double完全能够准确地表示整数。

setting w.q.b = 7:
00 00 00 00 00 00 1c 40 05 00 00 00 00 00 00 00 
values are now 0 5 7.000000000000000

在再次将7写入double后,您可以看到第一个字节再次为00 ,现在printf语句的结果确实是7.0。

所以 - 你的理解是正确的。 问题在于你的诊断 - 数字不同但你看不到它。

通常,查找这些内容的好方法是将数字存储在临时变量中,然后查看差异。 那么你会很容易找到它。

如果运行以下代码,您可以看到更改的值: -

#include <stdio.h>

union job
{
    struct data
    {
         int x;
         double b;       
    }q;
    int a;
} w;

int main() {

    w.q.b=7;
    w.a=4;
    w.q.x=5;

    printf("%d %d %f",w.a,w.q.x,w.q.b);
    return 0;
 }

输出:5 5 7.000000

我稍微修改了联合内部的结构,但这解释了你的担忧。

实际上,指令wa = 4会覆盖wqb的数据。 这是你的记忆的样子:

After   w.q.b=7;    After   w.a=4;      After   w.q.x=5;

|0|1|0|0|0|0|0|0|   |0|1|0|0|0|0|0|0|   |0|1|0|0|0|0|0|0|   \       \
|0|0|0|1|1|1|0|0|   |0|0|1|1|1|0|0|0|   |0|0|1|1|1|0|0|0|   |   w.a |
|0|0|0|0|0|0|0|0|   |0|0|0|0|0|0|0|0|   |0|0|0|0|0|0|0|0|   |       |
|0|0|0|0|0|0|0|0|   |0|0|0|0|0|1|0|0|   |0|0|0|0|0|1|0|0|   /       |   w.q.b
|0|0|0|0|0|0|0|0|   |0|0|0|0|0|0|0|0|   |0|0|0|0|0|0|0|0|           |
|0|0|0|0|0|0|0|0|   |0|0|0|0|0|0|0|0|   |0|0|0|0|0|0|0|0|           |
|0|0|0|0|0|0|0|0|   |0|0|0|0|0|0|0|0|   |0|0|0|0|0|0|0|0|           |
|0|0|0|0|0|0|0|0|   |0|0|0|0|0|0|0|0|   |0|0|0|0|0|0|0|0|           /

-----------------   -----------------   -----------------

|0|0|0|0|0|0|0|0|   |0|0|0|0|0|0|0|0|   |0|0|0|0|0|0|0|0|   \
|0|0|0|0|0|0|0|0|   |0|0|0|0|0|0|0|0|   |0|0|0|0|0|0|0|0|   |   w.q.x
|0|0|0|0|0|0|0|0|   |0|0|0|0|0|0|0|0|   |0|0|0|0|0|0|0|0|   |
|0|0|0|0|0|0|0|0|   |0|0|0|0|0|0|0|0|   |0|0|0|0|0|1|0|1|   /

正如您所看到的,由于将4分配给前4个字节, wqb的第30位从0更改为1 ,但此更改太低,因为只有尾数部分受到影响且打印精度wqb未显示这种变化。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM