x86的c语言和arm64的c语言,深入理解linux C语言位段的实现(x86 & ARM)

首先,如果你不知道位段(也叫位域)的概念,请自行学习,本文不再赘述。

OK,废话不多说,先上测试代码:

#include

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 00000000xxxx xxxx)

第2条指令:将eax中的值与-16(11111111 11111111 11111111 1111 0000)进行位与操作,将结果存入eax中。所以此操作是将eax的最低4位清空。

此时:eax值:(00000000 00000000 00000000xxxx 0000)

第3条指令:将eax中的值与11进行位或操作,将结果存入eax中。也就是给eax的最低4位中写入11。

此时:eax值:(00000000 00000000 000000000000 1011)

第4条指令:将al(eax的最低8位:00001011)中的值回写到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 00000000xx xxxxxx0000 1011)

第2条指令:将ax(eax的低16位)中的值与-16369(11 00000000001111)进行位与操作,将结果存入ax中。所以此操作是将eax的4-13位清空。(该条指令操作数长度为16位)

此时:eax值:(00000000 00000000xx 0000000000 1011)

第3条指令:将al(eax的最低8位)中的值与-64(11000000)进行位或操作,将结果存入al中。也就是给eax的4-7位中写入12。(操作数长度为8位)

此时:eax值:(00000000 00000000xx 0000001100 1011)

第4条指令:将ax(eax的低16位:xx00000011001011)中的值回写到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值:(xxxxxxxxxxxxxxxxxx 00000011001011)第2条指令:将eax中的值与-1032193(111111111111000000 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 00000011001011)

第4条指令:将eax中的值:(xxxxxxxxxxxx 001011 00000011001011)写到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 00000000xxxxxxxxxxxx0010)第2条指令:将eax中的值与15(00000000 00000000 0000000000001111)进行位与操作,将结果存入eax中。所以此操作是将eax的5- 31位清空。

此时:eax值:(00000000 000000000000000000001111)

第3条指令:将eax中的值与-32 (1110 0000)进行位或操作,将结果存入eax中。也就是给eax的4-7位中写入14。(操作数长度为8位)此时:eax值: (00000000 00000000 0000000011100010)第4条指令:将ax(eax的低16位:0000000011100010)中的值回写到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

待补充.....(其实原理差不多只不过指令集机不一样,有时间再写)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值