接着自己发布的一段code 就是强制转换后的指针变量将会方位不同长度的内存变量。后来又想到一个问题:
int a 是32位
char a 是8 位
那么在32位的变量处理上和8位的变量处理上会有什么区别?
其实主要想要搞明白对于一个已知位宽的芯片来说(32位),怎么区分写到寄存器或是外设上的数据是使用了低16位,还是低8位,还是32位。
0 7 15 23 31
----------------------------------------------------
B[0] B[1] B[2] B[3]
----------------------------------------------------
int a 占有32位 char a 只有1位。
分析:
1,寄存器
记住以上区别开始自己写了简单的的c语言函数在Fedora下自己编译,然后是有objdump查看了汇编。发现确实不同。
c code:
int.c
#include <stdio.h>
main()
{
int a = 0;
}
char.c
#include <stdio.h>
main()
{
char a = ‘0’;
}
然后分别编译查看汇编,发现了一下不同:
int
....................
080483b4 <main>:
80483b4: 55 push %ebp
80483b5: 89 e5 mov %esp,%ebp
80483b7: 83 ec 10 sub $0x10,%esp
80483ba: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp)
80483c1: c9 leave
80483c2: c3 ret
80483c3: 90 nop
....................
char
....................
080483b4 <main>:
80483b4: 55 push %ebp
80483b5: 89 e5 mov %esp,%ebp
80483b7: 83 ec 10 sub $0x10,%esp
80483ba: c6 45 ff 30 movb $0x30,-0x1(%ebp)
80483be: c9 leave
80483bf: c3 ret
.....................
0x30就是十进制的48,查asic表的‘0’。看了这里不难发现mov指令还有一个变种叫做movl。32位的变量都是使用这类指令传输与寄存器的。以上的分析主要是局部变量。局部变量在栈区,因此使用寄存器就够了。但是如果要使用外设或者是内存中的某一个确定的位置又是什么情况。看看下面的分析。
2,外设
这段话查看自网上的高手:computer00。但是注意了寄存器的实验是intel的PC上。一下这个片段是来自ARM的编译器。
例如,你声明一个char型的变量c,它只有8bit,做一个自加操作,c++,那么编译的代码是先进行加1操作,然后再使用与操作,将高24位清0.以下是来自keil对ADuC7026 ARM7编译的结果:
51: unsigned long int i;
52:
53: unsigned char c;
54:
0x000802AC E92D4000 STMDB R13!,{R14}
55: c++;
0x000802B0 E2833001 ADD R3,R3,#0x00000001
0x000802B4 E20330FF AND R3,R3,#0x000000FF
56: i++;
0x000802B8 E2844001 ADD R4,R4,#0x00000001
57: c--;
0x000802BC E2433001 SUB R3,R3,#0x00000001
0x000802C0 E20330FF AND R3,R3,#0x000000FF
58: i--;
0x000802C4 E2444001 SUB R4,R4,#0x00000001
i是32位的,c是8位的,一看编译结果便知.
由于这个是临时变量,保存在寄存器中,所以需要这样的操作。如果是保存在内存中的变量,由于有LDRB(字节加载)和STRB(字节存储)指令,所以可以不用与操作.
56: c++;
0x000802B8 E59F02F8 LDR R0,[PC,#0x02F8]
0x000802BC E5D01000 LDRB R1,[R0]
0x000802C0 E2811001 ADD R1,R1,#0x00000001
0x000802C4 E5C01000 STRB R1,[R0]
57: i++;
0x000802C8 E59F02EC LDR R0,[PC,#0x02EC]
0x000802CC E5901000 LDR R1,[R0]
0x000802D0 E2811001 ADD R1,R1,#0x00000001
0x000802D4 E5801000 STR R1,[R0]
58: c--;
0x000802D8 E59F02D8 LDR R0,[PC,#0x02D8]
0x000802DC E5D01000 LDRB R1,[R0]
0x000802E0 E2411001 SUB R1,R1,#0x00000001
0x000802E4 E5C01000 STRB R1,[R0]
59: i--;
0x000802E8 E59F02CC LDR R0,[PC,#0x02CC]
0x000802EC E5901000 LDR R1,[R0]
0x000802F0 E2411001 SUB R1,R1,#0x00000001
0x000802F4 E5801000 STR R1,[R0]
由于有些编译器会把变量加载到寄存器中操作,特别是对于循环操作等,因此也会增加类似的与操作指令.所以,有时候为了加快速度,宁愿选用32bit型的数据,而不用其它类型的。
看了以上文章清楚了吧。
3,总结
1,其实编译器会针对不同的变量类型选择适当的指令。32位还是8位的。看看ARM的Instruction TRM,可以发现其实在ldr std指令都是有变形的,比如ldrh ldrb等,这些指令就是针对不同位长的变量而来。
2,对于不同的表达类型来说,比如指针的强制转换,编译器也是会适当调整。不得不佩服编译器的本领。如果没有编译器。计算机可能还停留在010101010的枯燥乏味的世界。
希望高手指点。
谢谢