首先,如果你不知道位段(也叫位域)的概念,请自行学习,本文不再赘述。
OK,废话不多说,先上测试代码:
-
#include <stdio.h>
-
-
struct test {
-
int a:4; //4 bit
-
int b:10; //10 bit
-
int c:6; //6 bit
-
int d:12; //12 bit
-
}tmp;
-
-
int main(void)
-
{
-
tmp.a = 11;
-
tmp.b = 12;
-
tmp.c = 13;
-
tmp.d = 14;
-
-
printf("sizeof = %d\n", sizeof(struct test));
-
-
return 0;
-
}
首先,定义一个结构体变量tmp,该结构体包含四个位段元素,主函数分别对这个结构体变量的4个成员赋值,接下来我们使用GCC将该段代码编译成汇编文件。
-
yunyafeng@x3650-m4-ip202:~$ vim test.c
-
yunyafeng@x3650-m4-ip202:~$ gcc -S test.c
-
yunyafeng@x3650-m4-ip202:~$
生成的汇编文件代码如下:
-
.file "test.c"
-
.comm tmp,4,4 @这个地方就是我们的tmp变量,一共占4个字节的空间
-
.section .rodata
-
.LC0:
-
.string "sizeof = %d\n"
-
.text
-
.globl main
-
.type main, @function
-
main: @main函数开始
-
.LFB0:
-
.cfi_startproc
-
pushl %ebp
-
.cfi_def_cfa_offset 8
-
.cfi_offset 5, -8
-
movl %esp, %ebp
-
.cfi_def_cfa_register 5
-
andl $-16, %esp
-
subl $16, %esp @在这之前都是main函数执行的准备工作
-
-
@整个结构体的赋值是从这里开始
-
movzbl tmp, %eax
-
andl $-16, %eax
-
orl $11, %eax
-
movb %al, tmp
-
movzwl tmp, %eax
-
andw $-16369, %ax
-
orb $-64, %al
-
movw %ax, tmp
-
movl tmp, %eax
-
andl $-1032193, %eax
-
orl $212992, %eax
-
movl %eax, tmp
-
movzwl tmp+2, %eax
-
andl $15, %eax
-
orb $-32, %al
-
movw %ax, tmp+2
-
@结构体赋值到此结束
-
-
movl $4, 4(%esp)
-
movl $.LC0, (%esp)
-
call printf
-
movl $0, %eax
-
leave
-
.cfi_restore 5
-
.cfi_def_cfa 4, 4
-
ret
-
.cfi_endproc
-
.LFE0:
-
.size main, .-main
-
.ident "GCC: (Ubuntu 4.8.1-2ubuntu1~12.04) 4.8.1"
-
.section .note.GNU-stack,"",@progbits
首先我们应该清楚tmp这个变量在内存中到底是则么存的,看下图:
为了方便我们用以下四种颜色(不要问我什么颜色 我也不认识 能看到就好)分别代表tmp中的 a ,b, c, d元素。
初始值我们也不知道是多少就用x表示
31 (高地址)----------->
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
c
|
c
|
c
|
c
|
c
|
c
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
a
|
a
|
a
|
a
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
结构体的第1个元素位于最低地址处,依次类推。
好的 我们接下来一步一步的分析,首先是结构体中的第1个元素
tmp.a的赋值过程;
-
movzbl tmp, %eax
-
andl $-16, %eax
-
orl $11, %eax
-
movb %al, tmp
这4行代码为第一个元素赋值的过程,其中:
第1条指令:将tmp的第一个字节以高位补0的方式放入eax寄存器中。
此时:eax值:(00000000 00000000 00000000 xxxx xxxx)
第2条指令:将eax中的值与-16(11111111 11111111 11111111 1111 0000)进行位与操作,将结果存入eax中。所以此操作是将eax的最低4位清空。
此时:eax值:(00000000 00000000 00000000 xxxx 0000)
第3条指令:将eax中的值与11进行位或操作,将结果存入eax中。也就是给eax的最低4位中写入11。
此时:eax值:(00000000 00000000 00000000 0000 1011)
第4条指令:将al(eax的最低8位:0000 1011)中的值回写到tmp的第一个字节中,此时内存中的值为:
31 (高地址)----------->
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
c
|
c
|
c
|
c
|
c
|
c
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
a
|
a
|
a
|
a
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
0
|
0
|
0
|
0
|
1
|
0
|
1
|
1
|
可见第一条赋值语句
tmp.a = 11;执行完成。
第2个元素
tmp.b的赋值过程:
-
movzwl tmp, %eax
-
andw $-16369, %ax
-
orb $-64, %al
-
movw %ax, tmp
第1条指令:将tmp的前2个字节以高位补0的方式放入eax寄存器中。
此时:eax值:(00000000 00000000 xx xxxxxx0000 1011)
第2条指令:将ax(eax的低16位)中的值与-16369(11 0000000000 1111)进行位与操作,将结果存入ax中。所以此操作是将eax的4-13位清空。(该条指令操作数长度为16位)
此时:eax值:(00000000 00000000 xx 0000000000 1011)
第3条指令:将al(eax的最低8位)中的值与-64(1100 0000)进行位或操作,将结果存入al中。也就是给eax的4-7位中写入12。(操作数长度为8位)
此时:eax值:(00000000 00000000 xx 0000001100 1011)
第4条指令:将ax(eax的低16位:xx 0000001100 1011)中的值回写到tmp的前2个字节中,此时内存中的值为:
31 (高地址)----------->
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
c
|
c
|
c
|
c
|
c
|
c
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
a
|
a
|
a
|
a
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
0
|
0
|
0
|
0
|
0
|
0
|
1
|
1
|
0
|
0
|
1
|
0
|
1
|
1
|
tmp.b = 12;执行完成。
第3个元素tmp.c的赋值过程:
-
movl tmp, %eax
-
andl $-1032193, %eax
-
orl $212992, %eax
-
movl %eax, tmp
第1条指令:将tmp里的值放入eax寄存器中。
此时:eax值: (xxxxxxxxxxxx xxxxxx 0000001100 1011)
第2条指令:将eax中的值与-1032193(111111111111 000000 1111111111 1111)进行位与操作,将结果存入eax中。所以此操作是将eax的14-19位清空。
此时:eax值: (xxxxxxxxxxxx 000000 0000001100 1011)
第3条指令:将eax中的值与212992 (000000000000 001011 0000000000 0000)进行位或操作,将结果存入eax中。也就是给eax的14-19位中写入12。(操作数长度为8位)
此时:eax值: (xxxxxxxxxxxx 001011 0000001100 1011)
第4条指令:将eax中的值:(xxxxxxxxxxxx 001011 0000001100 1011)写到tmp的内存中,此时内存中的值为:
31 (高地址)----------->
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
c
|
c
|
c
|
c
|
c
|
c
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
a
|
a
|
a
|
a
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
x
|
0
|
0
|
1
|
0
|
1
|
1
|
0
|
0
|
0
|
0
|
0
|
0
|
1
|
1
|
0
|
0
|
1
|
0
|
1
|
1
|
tmp.c = 13;执行完成。
第4个元素tmp.d的赋值过程:
-
movzwl tmp+2, %eax
-
andl $15, %eax
-
orb $-32, %al
-
movw %ax, tmp+2
第1条指令:将tmp+2的(tmp的最高16位)的值以高位补0的方式放eax寄存器中。
此时:eax值: (00000000 00000000 xxxxxxxxxxxx 0010)
第2条指令:将eax中的值与15(00000000 00000000 000000000000 1111)进行位与操作,将结果存入eax中。所以此操作是将eax的5- 31位清空。
此时:eax值: (00000000 00000000 000000000000 1111)
第3条指令:将eax中的值与-32 (1110 0000)进行位或操作,将结果存入eax中。也就是给eax的4-7位中写入14。(操作数长度为8位)
此时:eax值: (00000000 00000000 000000001110 0010)
第4条指令:将ax(eax的低16位: 000000001110 0010)中的值回写到tmp+2地址处(tmp的高16位),此时内存中的值为:
31 (高地址)----------->
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
d
|
c
|
c
|
c
|
c
|
c
|
c
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
b
|
a
|
a
|
a
|
a
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
1
|
1
|
1
|
0
|
0
|
0
|
1
|
0
|
1
|
1
|
0
|
0
|
0
|
0
|
0
|
0
|
1
|
1
|
0
|
0
|
1
|
0
|
1
|
1
|
tmp.d = 14;执行完成。
到此结构体的所有成员赋值完毕。
总结:
在linux下, gcc对于位段的实现是靠位运算完成的,一个位段的赋值过成需要分解成多条指令来完成。可见位段的使用其实是节省了内存空间,但是节省空间是靠牺牲速度换来的。所以对于位段的使用还要根据实际情况来决定。
本文地址:http://blog.chinaunix.net/uid-30195520-id-4947212.html
待补充.....(其实原理差不多只不过指令集机不一样,有时间再写)
阅读(1445) | 评论(0) | 转发(0) |