联合填充值在C / GCC 4.6.3中

时间:2014-04-22 23:39:50

标签: c gcc padding unions

我的代码初始化了6个结构,2个联合,并使用'dump'函数显示它们分配的字节的地址,同样是这些字节中的值。

短代码:

#include <stdio.h>
// #DEFINE PD = padding

void dump (void *p, int n);

/* (...) */

union U1
{
    int i;
    char c[5];
};

/*  has 8 bytes of information, organized as follows:
    | i/c[0] | i/c[1] | i/c[2] | i/c[3] | PD/c[4] | PD | PD | PD |                                      */

union U2
{
    short s;
    char c[5];
};

/*  has 6 bytes of information, organized as follows:
    | s/c[0] | s/c[1] | PD/c[2] | PD/c[3] | PD/c[4] | PD |                                          */



int main (void)
{
    int i;

    union U1 u1;
    union U2 u2;

    /* (...) */

         u1.i = 0x01020304;      // initializes int
    printf("\nu1 (int)\n");
        dump(&u1, sizeof(u1));
        for (i=0;i<5;i++)        // initializes char
            u1.c[i] = 0xcc;
    printf("u1 (char)\n");
        dump(&u1, sizeof(u1));

        u2.s = 0x0102;           // initializes short
    printf("\nu2 (short)\n");
        dump(&u2, sizeof(u2));   
        for (i=0;i<5;i++)        // initializes char
            u2.c[i] = 0xcc;
    printf("u2 (char)\n");
        dump(&u2, sizeof(u2));

    return 0;
} 

从所有结构和第一个联合中我得到了预期的字节数和值 - 对于所有填充字节都是00 - 但是从最后一个联合我得到了这个:

u2 (short)
0x7fff825a05a0 - 02
0x7fff825a05a1 - 01
0x7fff825a05a2 - 5A
0x7fff825a05a3 - 82
0x7fff825a05a4 - FF
0x7fff825a05a5 - 7F
u2 (char)
0x7fff825a05a0 - CC
0x7fff825a05a1 - CC
0x7fff825a05a2 - CC
0x7fff825a05a3 - CC
0x7fff825a05a4 - CC
0x7fff825a05a5 - 7F

短消息的2个中间字节的值是随机的 - 可能是随机内存,每次运行时都会发生变化 - 最后2个短字节和最后一个字符数组都是固定的。

为什么我会得到这个值?不应该所有的填充字节都是0x00吗? 即使是第一个联盟也顺利,从int到short的变化这一事实有什么变化? 为什么将部分内存垃圾和部分固定值用短变量初始化?

我认为这个问题的答案是理论上的,如果可以的话,请您引用参考文献?那会很棒。

请原谅我可能的巨型职位,我的第一个在这里。 :)

完整代码:

#include <stdio.h>
// #DEFINE PD = padding

void dump (void *p, int n)
{
     unsigned char *p1 = p;

     while (n--)
     {
          printf("%p - %02X\n",p1, *p1);
          p1++;
     }
}

struct X1
{
    char c1;
    int i;
    char c2;
} x1 = {0xc1, 0x01020304, 0xc2};

/*  possui 12 bytes de informação, organizados em:
    | c1 | PD | PD | PD | i | i | i | i | c2 | PD | PD | PD |                                       */

struct X2
{
    int i;
    char c;
} x2 = {0x01020304, 0xc2};

/*  possui 8 bytes de informação, organizados em:
    | i | i | i | i | c | PD | PD | PD |                                                            */

struct X3
{
    int i;
    char c1;
    char c2;
} x3 = {0x01020304, 0xc1, 0xc2};

/*  possui 8 bytes de informação, organizados em:
    | i | i | i | i | c1 | c2 | PD | PD |                                                           */

struct X4
{
    struct X2 x;
    char c;
} x4 = {{0x01020304, 0xc1}, 0xc2};

/*  possui 8 bytes de informação, organizados em:
    | X2.i | X2.i | X2.i | X2.i | X2.c | PD | PD | PD | c | PD | PD | PD |                          */

struct X5
{
    char c1;
    char c2;
    char c3;
} x5 = {0xc1, 0xc2, 0xc3};

/*  possui 3 bytes de informação, organizados em:
    | c1 | c2 | c3 |                                                                            */

struct X6
{
    short s1;
    int i;
    char c[3];
    short s2;
} x6 = {0x0102, 0x01020304, {0xc1, 0xc2, 0xc3}, 0x0102};

/*  possui 16 bytes de informação, organizados em:
    | s1 | s1 | PD | PD | i | i | i | i | c[0] | c[1] | c[2] | PD | s2 | s2| PD | PD |              */


union U1
{
    int i;
    char c[5];
};

/*  possui 8 bytes de informação, organizados em:
    | i/c[0] | i/c[1] | i/c[2] | i/c[3] | PD/c[4] | PD | PD | PD |                                      */

union U2
{
    short s;
    char c[5];
};

/*  possui 8 bytes de informação, organizados em:
    | s/c[0] | s/c[1] | PD/c[2] | PD/c[3] | PD/c[4] | PD | PD | PD |                                            */



int main (void)
{
    int i;

    union U1 u1;
    union U2 u2;

    printf("\nx1: \n");
        dump(&x1, sizeof(x1));
    printf("\nx2: \n");
        dump(&x2, sizeof(x2));
    printf("\nx3: \n");
        dump(&x3, sizeof(x3));
    printf("\nx4: \n");
        dump(&x4, sizeof(x4));
    printf("\nx5: \n");
        dump(&x5, sizeof(x5));
    printf("\nx6: \n");
        dump(&x6, sizeof(x6));

         u1.i = 0x01020304;
    printf("\nu1 (int)\n");
        dump(&u1, sizeof(u1));
        for (i=0;i<5;i++)
            u1.c[i] = 0xcc;
    printf("u1 (char)\n");
        dump(&u1, sizeof(u1));

        u2.s = 0x0102;
    printf("\nu2 (short)\n");
        dump(&u2, sizeof(u2));
        for (i=0;i<5;i++)
            u2.c[i] = 0xcc;
    printf("u2 (char)\n");
        dump(&u2, sizeof(u2));

    return 0;
    }

完整输出:

x1: 
0x601030 - C1
0x601031 - 00
0x601032 - 00
0x601033 - 00
0x601034 - 04
0x601035 - 03
0x601036 - 02
0x601037 - 01
0x601038 - C2
0x601039 - 00
0x60103a - 00
0x60103b - 00

x2: 
0x60103c - 04
0x60103d - 03
0x60103e - 02
0x60103f - 01
0x601040 - C2
0x601041 - 00
0x601042 - 00
0x601043 - 00

x3: 
0x601044 - 04
0x601045 - 03
0x601046 - 02
0x601047 - 01
0x601048 - C1
0x601049 - C2
0x60104a - 00
0x60104b - 00

x4: 
0x60104c - 04
0x60104d - 03
0x60104e - 02
0x60104f - 01
0x601050 - C1
0x601051 - 00
0x601052 - 00
0x601053 - 00
0x601054 - C2
0x601055 - 00
0x601056 - 00
0x601057 - 00

x5: 
0x601058 - C1
0x601059 - C2
0x60105a - C3

x6: 
0x601060 - 02
0x601061 - 01
0x601062 - 00
0x601063 - 00
0x601064 - 04
0x601065 - 03
0x601066 - 02
0x601067 - 01
0x601068 - C1
0x601069 - C2
0x60106a - C3
0x60106b - 00
0x60106c - 02
0x60106d - 01
0x60106e - 00
0x60106f - 00

u1 (int)
0x7fff825a0590 - 04
0x7fff825a0591 - 03
0x7fff825a0592 - 02
0x7fff825a0593 - 01
0x7fff825a0594 - 00
0x7fff825a0595 - 00
0x7fff825a0596 - 00
0x7fff825a0597 - 00
u1 (char)
0x7fff825a0590 - CC
0x7fff825a0591 - CC
0x7fff825a0592 - CC
0x7fff825a0593 - CC
0x7fff825a0594 - CC
0x7fff825a0595 - 00
0x7fff825a0596 - 00
0x7fff825a0597 - 00

u2 (short)
0x7fff825a05a0 - 02
0x7fff825a05a1 - 01
0x7fff825a05a2 - 5A
0x7fff825a05a3 - 82
0x7fff825a05a4 - FF
0x7fff825a05a5 - 7F
u2 (char)
0x7fff825a05a0 - CC
0x7fff825a05a1 - CC
0x7fff825a05a2 - CC
0x7fff825a05a3 - CC
0x7fff825a05a4 - CC
0x7fff825a05a5 - 7F

3 个答案:

答案 0 :(得分:3)

根据C99标准,6.2.6.1p7:

  

当值存储在结构或联合类型的对象中时,   包括在成员对象中,对象表示的字节   与任何填充字节对应的值都是未指定的值。

答案 1 :(得分:2)

您的工会处于堆叠状态并且不是全局的,在您开始使用它们之前,没有任何关于它们被初始化的期望。按照官方的说法,你永远不应该阅读你没有先写过的那个工会或任何变量。

对于结构中的填充程序的多少或状态,或者使用结构或没有结构的联合的自然结合,确实没有有效期望。

如果你让它们全局化,那么它们会有一些有效的期望让它们开始归零,但我个人不赞同这种期望并尝试在我第一次读它之前总是显式设置变量(写) 。一旦你开始搞乱联盟中的项目,那么所有的赌注都会影响到什么。

修改

你的工会是非静态的局部变量,所以它们在堆栈上,因此在你开始访问它们之前不会期望C编译器进行初始化。

联合中项目的唯一有效用途是回读您写入的最近访问过的路径。

所以

union U1 u1;
union U2 u2;

/* (...) */

     u1.i = 0x01020304;      // initializes int

所以这里唯一有效的用例是回读u1.i.对于任何u1.c []值,你不应该对此有任何期望。并且你不应该对联盟使用的整个内存中的任何其他填充有任何期望。 C编译器唯一需要确保的是,如果你设置了u1.i,那么当你读回它时假设你没有在这种情况下使用u1.c修改了联合,那么你将得到你所写的内容。

    for (i=0;i<5;i++)        // initializes char
        u1.c[i] = 0xcc;

同样的答案,此时唯一有效的联合读取是u1.c []项,读取u1.i没有有效的预期。同样转储整个联合可以有填充,使得字符串有意义,并且读取类似的项目(u1.c [n])就是你所写的。

u2.s = 0x0102;           // initializes short

然后访问u2.c []毫无意义,并且没有有效的期望。转储联合唯一的期望是联合中某处的两个连续字节是该架构的字节序中的0x01和0x02。

for (i=0;i<5;i++)        // initializes char
u2.c[i] = 0xcc;

然后访问u2.s毫无意义,并且没有有效的预期。转储联合唯一的期望是联合中的某个地方是正确顺序的字节0xcc,0xcd,0xce,0xcf,0xd0,在联合中的位置和填充的值你应该没有期望。

现在说我经常能够不正确地使用工会来捅浮点数并且它往往(到目前为止一直)工作,但我知道那些代码可能会在某一天停止工作由不同版本的相同编译器编译,不同的编译器或具有不同编译选项的相同编译器。我希望有一天它可能会失败。

答案 2 :(得分:1)

您的工会u1u2 自动并存储在堆叠中。默认情况下,自动变量在C中初始化为垃圾。您在转储中看到的是堆栈上的旧值,在调用main()之前存在这些值。它们是半随机的,取决于C运行时库在调用main()之前所执行的操作。

代码中的结构x1 - x6不同,它们是 static ,静态变量被C标准初始化为零。

函数内定义的变量是自动(除非声明为静态),但函数外的变量只能是静态。为了使答案更加完整,还有另一个存储类,或动态内存,可通过malloc()/ free()访问。在整个程序运行期间,只有静态变量在内存中具有固定位置,其他位置在相应函数返回时松散,或者释放堆块时。 这些都是C程序中常见的错误来源。