本机尺寸与结构体对齐

本机尺寸

如果本机是32位的,那么对32位的支持是最好的

在很多的书上都能看到类似这样的代码

#include<stdio.h>
void Function(char x,char y)
{
    char c=x+y;
}
void main()
{
    char a=1;
    char b=2;
    Function(a,b);
}

我们通过反汇编会发现

1:    #include<stdio.h>
2:    void Function(char x,char y)
3:    {
00401020   push        ebp
00401021   mov         ebp,esp
00401023   sub         esp,44h
00401026   push        ebx
00401027   push        esi
00401028   push        edi
00401029   lea         edi,[ebp-44h]
0040102C   mov         ecx,11h
00401031   mov         eax,0CCCCCCCCh
00401036   rep stos    dword ptr [edi]
4:        char c=x+y;
00401038   movsx       eax,byte ptr [ebp+8]
0040103C   movsx       ecx,byte ptr [ebp+0Ch]
00401040   add         eax,ecx
00401042   mov         byte ptr [ebp-4],al
5:    }
00401045   pop         edi
00401046   pop         esi
00401047   pop         ebx
00401048   mov         esp,ebp
0040104A   pop         ebp
0040104B   ret
--- No source file  ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0040104C   int         3
0040104D   int         3
0040104E   int         3
0040104F   int         3
--- C:\Windows\System32\a.cpp  -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
6:    void main()
7:    {
00401050   push        ebp
00401051   mov         ebp,esp
00401053   sub         esp,48h
00401056   push        ebx
00401057   push        esi
00401058   push        edi
00401059   lea         edi,[ebp-48h]
0040105C   mov         ecx,12h
00401061   mov         eax,0CCCCCCCCh
00401066   rep stos    dword ptr [edi]
8:        char a=1;
00401068   mov         byte ptr [ebp-4],1
9:        char b=2;
0040106C   mov         byte ptr [ebp-8],2
10:       Function(a,b);
00401070   mov         al,byte ptr [ebp-8]
00401073   pusheax
00401074   mov         cl,byte ptr [ebp-4]
00401077   pushecx
00401078   call        @ILT+0(Function) (00401005)
0040107D   add         esp,8
11:   }
00401080   pop         edi
00401081   pop         esi
00401082   pop         ebx
00401083   add         esp,48h
00401086   cmp         ebp,esp
00401088   call        __chkesp (004010a0)
0040108D   mov         esp,ebp
0040108F   pop         ebp
00401090   ret

蓝色部分是传递进去的参数

然后我们发现,尽管定义的是char类型,压栈的却是eax而并不是al。

在32位的系统中,系统默认最合适的数据类型,就是32个bit,即4字节。同理,64位的系统就是8字节。

也就是说,如果使用小于4个字节的局部变量来进行参数传递,vc编译器仍然会按照4个字节来进行传递,但是多余的部分并不会使用。

 

 

从上面我们能看到,当创建一个小于4字节局部变量时,编译器为我们分配的内存空间为0x44。

当我们创建两个局部变量时

1:    #include<stdio.h>
2:    void Function(char x,char y)
3:    {
00401020   push        ebp
00401021   mov         ebp,esp
00401023   sub         esp,44h
00401026   push        ebx
00401027   push        esi
00401028   push        edi
00401029   lea         edi,[ebp-44h]
0040102C   mov         ecx,11h
00401031   mov         eax,0CCCCCCCCh
00401036   rep stos    dword ptr [edi]
4:        char c=x+y;
00401038   movsx       eax,byte ptr [ebp+8]
0040103C   movsx       ecx,byte ptr [ebp+0Ch]
00401040   add         eax,ecx
00401042   mov         byte ptr [ebp-4],al
5:        char d;
6:    }
00401045   pop         edi
00401046   pop         esi
00401047   pop         ebx
00401048   mov         esp,ebp
0040104A   pop         ebp
0040104B   ret
--- No source file  ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0040104C   int         3
0040104D   int         3
0040104E   int         3
0040104F   int         3
--- C:\Windows\System32\a.cpp  -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
7:    void main()
8:    {
00401050   push        ebp
00401051   mov         ebp,esp
00401053   sub         esp,48h
00401056   push        ebx
00401057   push        esi
00401058   push        edi
00401059   lea         edi,[ebp-48h]
0040105C   mov         ecx,12h
00401061   mov         eax,0CCCCCCCCh
00401066   rep stos    dword ptr [edi]
9:        char a=1;
00401068   mov         byte ptr [ebp-4],1
10:       char b=2;
0040106C   mov         byte ptr [ebp-8],2
11:       Function(a,b);
00401070   mov         al,byte ptr [ebp-8]
00401073   push        eax
00401074   mov         cl,byte ptr [ebp-4]
00401077   push        ecx
00401078   call        @ILT+0(Function) (00401005)
0040107D   add         esp,8
12:   }
00401080   pop         edi
00401081   pop         esi
00401082   pop         ebx
00401083   add         esp,48h
00401086   cmp         ebp,esp
00401088   call        __chkesp (004010a0)
0040108D   mov         esp,ebp
0040108F   pop         ebp
00401090   ret

缓冲区变为0x48了

再尝试创建一个大于4字节的数

1:    #include<stdio.h>
2:    void Function(char x,char y)
3:    {
00401020   push        ebp
00401021   mov         ebp,esp
00401023   sub         esp,4Ch
00401026   push        ebx
00401027   push        esi
00401028   push        edi
00401029   lea         edi,[ebp-4Ch]
0040102C   mov         ecx,13h
00401031   mov         eax,0CCCCCCCCh
00401036   rep stos    dword ptr [edi]
4:        char c=x+y;
00401038   movsx       eax,byte ptr [ebp+8]
0040103C   movsx       ecx,byte ptr [ebp+0Ch]
00401040   add         eax,ecx
00401042   mov         byte ptr [ebp-4],al
5:        __int64 d;
6:    }
00401045   pop         edi
00401046   pop         esi
00401047   pop         ebx
00401048   mov         esp,ebp
0040104A   pop         ebp
0040104B   ret
--- No source file  ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0040104C   int         3
0040104D   int         3
0040104E   int         3
0040104F   int         3
--- C:\Windows\System32\a.cpp  -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
7:    void main()
8:    {
00401050   push        ebp
00401051   mov         ebp,esp
00401053   sub         esp,48h
00401056   push        ebx
00401057   push        esi
00401058   push        edi
00401059   lea         edi,[ebp-48h]
0040105C   mov         ecx,12h
00401061   mov         eax,0CCCCCCCCh
00401066   rep stos    dword ptr [edi]
9:        char a=1;
00401068   mov         byte ptr [ebp-4],1
10:       char b=2;
0040106C   mov         byte ptr [ebp-8],2
11:       Function(a,b);
00401070   mov         al,byte ptr [ebp-8]
00401073   push        eax
00401074   mov         cl,byte ptr [ebp-4]
00401077   push        ecx
00401078   call        @ILT+0(Function) (00401005)
0040107D   add         esp,8
12:   }
00401080   pop         edi
00401081   pop         esi
00401082   pop         ebx
00401083   add         esp,48h
00401086   cmp         ebp,esp
00401088   call        __chkesp (004010a0)
0040108D   mov         esp,ebp
0040108F   pop         ebp
00401090   ret

这里就变成0x4c,因为__int64是8位的,所以就是0x48+4=0x4c了。

总结:

0x00.在32位系统上创建小于32位的局部变量是不合适的,在空间分配时仍然会按照32位来进行分配。但多余部分并不会使用。所以尽量避免使用小于32位的局部变量。

0x01.在vc6.0中调用函数时,每创建一个小于32位的局部变量,就会分配4个字节的空间。如果创建大于32位的局部变量,就会分配该局部变量实际的空间。

总的来说就是一句话,在32位的windows系统,使用vc6编程时尽量不要创建小于4个字节的局部变量,整数类型的参数,一律int类型。

 

结构体对齐

在计算机中,结构体通常是字节对齐的。这样在数据的存储方面能达到最好的效率。空间和时间的取舍问题~~~

例子:

1:

#include<stdio.h>
struct test
{
    char a;
    int b;
    char c;
};
int main()
{
    printf("%d",sizeof(test));
    return 0;
}

2.

#include<stdio.h>
struct test
{
    int b;
    char a;
    char c;
};
int main()
{
    printf("%d",sizeof(test));
    return 0;
}

此时,前者打印出来的大小是12,而后者打印的结果却是8。

 

如果想控制这个对齐字节可以通过加上两行代码。

1 #pragma pack(1)
2 struct Test
3 {
4     int a;
5     __int64 b;
6     char c;
7 }
8 #pragma pack()

此时对齐的字节就是1了。

 

那么当不写#pragma时,又是对齐的字节又是多少呢?

这是和编译器有关系。比如我使用的是vc6.0,可以在这里看到:

不建议修改这里参数。

 

结构体对齐又可以叫字节对齐。大致遵循以下四个原则。

原则1:数据成员对齐规则,结构的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍开始存储)
原则2:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足要补齐。
原则3:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double 等元素,那b应该从8的整数倍开始存储)
原则4:对齐参数如果比结构体成员的sizeof值小,该成员的偏移量应该以此值为准。也就是说,结构体成员的偏移量应该取二者的最小值

其实很好理解,随便一个例子。

struct test
{
    int b;
    char a;
    char c;
};

这个就相当于这样。所以这个是8。

再来一个,同样还是上文中提到的。

struct test
{
    char a;
    int b;
    char c;
};

总的来说。一个结构体中,所占空间最大的类型,和对齐字节比较。取较小的,记为x。然后该结构体第一个成员记为s1,第二个记为s2。。。。。

然后x-s1与s2比较,小于的话重新加一个x,另起一行,而第一个成员的后面就空着浪费掉了,被填充上数据cc,即int3。大于的话则变为x-s1-s2与s3比较。(自己理解的)

所以会有,结构体大小一定是最大成员大小的整数倍。

 

那么加入一个结构体中有另一个结构体该如何计算。

#include<stdio.h>
struct test1
{
    int a;
    char b;
};
struct test2
{
    test1 a;
    int b;
};
int main()
{
    printf("%d",sizeof(test2));
    return 0;
}

结果仍然是12。

这是因为test2实际就相当于这样一个结构体。(仍然是自己的理解,不过感觉是对的~~~错误欢迎指出)

struct test2
{
    int a;
    char b;
    int c;
};
posted @ 2018-03-04 18:52  newen  阅读(648)  评论(0编辑  收藏  举报