内存布局 - C联合

时间:2012-03-23 05:03:35

标签: c memory

我有一个三个整数的数组的联合类型(每个4个字节),一个浮点数(4个字节),一个双精度数(8个字节)和一个字符(1个字节)。

如果我为每个三个整数元素分配0x31313131然后打印了union的字符,我会得到数字1.为什么?

我不明白输出我知道3 0x31313131的位是 001100010011000100110001001100010011000100110001001100010011000100110001001100010011000100110001

2 个答案:

答案 0 :(得分:3)

因为'1'== 0x31。您将其打印为字符,而不是整数。

因为它是一个联合所有的int和char共享相同的内存位置(在这种情况下float和double无关紧要)。因此,将0x31313131分配给int 确实会影响char值 - 没有什么比这更令人困惑。

答案 1 :(得分:1)

工会的每个成员都有相同的起始地址;不同的成员可能有不同的大小。整个联合的大小至少任何成员的最大大小;最后可能会有额外的填充以满足对齐要求。

将值0x31313131存储在union对象的前三个int大小的内存区域中。 0x31313131是4个字节,每个字节的值为0x31

然后通过访问字符成员读取第一个字节(来自偏移0)。该字节的值为0x31,恰好是ASCII和类似字符集中字符'1'的编码。 (如果您在基于EBCDIC的系统上运行程序,则会看到不同的结果。)

由于您没有向我们展示任何实际的源代码,我将根据您的描述:

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

void hex_dump(char *name, void *base, size_t size) {
    unsigned char *arr = base;
    char c = ' ';

    printf("%-8s : ", name);
    for (size_t i = 0; i < size; i ++) {
        printf("%02x", arr[i]);
        if (i < size - 1) {
            putchar(' ');
        }
        else {
            putchar('\n');
        }
    }
}

int main(void) {
    union u {
        int arr[3];
        float f;
        double d;
        char c;
    };

    union u obj;

    memset(&obj, 0xff, sizeof obj);

    obj.arr[0] = 0x31323334;
    obj.arr[1] = 0x35363738;
    obj.arr[2] = 0x393a3b3c;

    hex_dump("obj",     &obj,     sizeof obj);
    hex_dump("obj.arr", &obj.arr, sizeof obj.arr);
    hex_dump("obj.f",   &obj.f,   sizeof obj.f);
    hex_dump("obj.d",   &obj.d,   sizeof obj.d);
    hex_dump("obj.c",   &obj.c,   sizeof obj.c);

    printf("obj.c = %d = 0x%x = '%c'\n",
           (int)obj.c, (unsigned)obj.c, obj.c);

    return 0;
}

hex_dump函数通过以十六进制显示每个字节的值来转储任何对象的原始表示,而不管其类型如何。

我首先使用0xff字节填充union对象。然后,如您所述,我初始化int[3]成员arr的每个元素 - 但为了更清楚地显示正在发生的事情,我为每个字节使用不同的值。

我在一个系统上获得的输出(恰好是little-endian)是:

obj      : 34 33 32 31 38 37 36 35 3c 3b 3a 39 ff ff ff ff
obj.arr  : 34 33 32 31 38 37 36 35 3c 3b 3a 39
obj.f    : 34 33 32 31
obj.d    : 34 33 32 31 38 37 36 35
obj.c    : 34
obj.c = 52 = 0x34 = '4'

如您所见,每个成员的初始字节彼此一致,因为它们存储在同一个地方。尾随ff字节不受向arr赋值的影响(这不是唯一有效的行为;标准表示它们采用未指定的值)。由于系统是little-endian,因此每个int值的高位字节存储在内存中的最低位置。

big-endian系统的输出是:

obj      : 31 32 33 34 35 36 37 38 39 3a 3b 3c ff ff ff ff
obj.arr  : 31 32 33 34 35 36 37 38 39 3a 3b 3c
obj.f    : 31 32 33 34
obj.d    : 31 32 33 34 35 36 37 38
obj.c    : 31
obj.c = 49 = 0x31 = '1'

如您所见,每个int的高位字节位于内存中的最低位置。

在所有情况下,obj.c的值是obj.arr[0]第一个字节 - 它将是高阶或低阶字节,具体取决于关于字节序。

有一种很多方式可以在不同的系统中发生变化。 intfloatdouble的尺寸可能会有所不同。表示浮点数的方式可以变化(尽管此示例未显示)。甚至一个字节中的位数也可以变化;它至少是8,但可能更大。 (在你可能遇到的任何系统上都是8)。标准允许整数表示中的填充位;在我展示的例子中没有。