首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++中联合内的结构

C++中联合内的结构
EN

Stack Overflow用户
提问于 2014-01-30 05:03:28
回答 3查看 4.1K关注 0票数 4

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

代码语言:javascript
复制
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的值,然后修改q.x的值,然后a的值将被q.x覆盖。但在输出中,它仍然显示a和q.x的原始值。我不能理解为什么会发生这种情况?

EN

回答 3

Stack Overflow用户

发布于 2014-01-30 06:37:51

你的理解是正确的-数字应该改变。我采用了您的代码,并添加了更多代码,以向您展示到底发生了什么。

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

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

代码语言:javascript
复制
aaaa
bbbbbbbbxxxx

如您所见,b的前四个字节与a重叠。这将被证明是重要的。

现在我们来看看double的典型存储方式(我是从64位英特尔架构的Mac的角度来写这篇文章的。碰巧内存中的格式确实是IEEE754格式):

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

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

代码语言:javascript
复制
#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;
}

和输出:

代码语言:javascript
复制
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位边界上对齐。

代码语言:javascript
复制
setting w.q.b = 7:
00 00 00 00 00 00 1c 40 00 00 00 00 00 00 00 00 

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

代码语言:javascript
复制
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时,我们已经修改了第一个字节。这对应于尾数的最低有效位,现在是(十六进制):

代码语言:javascript
复制
00 00 00 00 00 00 04

现在尾数的格式意味着这个数字左边有一个1;所以把最后一位从0改为4 in只改变了一个很小的数字的大小-你需要查看第15个小数才能看到它。

代码语言:javascript
复制
setting w.q.x = 5:
04 00 00 00 00 00 1c 40 05 00 00 00 00 00 00 00 

5被写在它自己的小空间中

代码语言:javascript
复制
values are now 4 5 7.000000000000004

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

代码语言:javascript
复制
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。

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

通常,查找这些东西的一个好方法是将数字存储在一个临时变量中,并查看差异。那么你就会很容易地找到它。

票数 7
EN

Stack Overflow用户

发布于 2014-01-30 05:39:29

如果你运行下面的代码,你可以看到改变的值:

代码语言:javascript
复制
#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

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

票数 1
EN

Stack Overflow用户

发布于 2014-01-30 06:44:46

实际上,指令w.a = 4覆盖了w.q.b的数据。下面是你的记忆:

代码语言:javascript
复制
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|   /

正如您可以看到的,由于将0分配给前4个字节,w.q.b的第30位从0更改为1,但这种更改太低,因为只有尾数部分受影响,并且打印w.q.b的精度没有显示这种更改。

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

https://stackoverflow.com/questions/21442600

复制
相关文章

相似问题

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