判断机器字节序
方法一:强制类型转换
int get_endian()
{
int a = 0x01;
char* p = (char*) &a;
return (*p == 0x01) ? 0 : 1; /*0: little, 1: big*/
}
方法二:
使用联合体,类似与强转
int get_endian()
{
union {
char c;
int i;
} a;
a.i = 0x01;
return (a.c == 0x01)? 0 : 1;
}
字节序转换
方法一:char*指针强转,需要一个char类型buf来接收
short short_2_big_endian(short parm)
{
char tmp_buf[2] = {0};
char *ptr = (char*)&parm;
if (get_endian() == 0)
{
tmp_buf[0] = *(ptr+1);
tmp_buf[1] = *ptr;
return *(short*)tmp_buf;
}
return parm;
}
方法二:移位操作,无需判断主机大小端
short short_2_big_endian(short parm)
{
char tmp_buf[2] = {0};
tmp_buf[0]= (parm >> 0) && 0xff;
tmp_buf[1]= (parm >> 8) && 0xff;
return *(short*)tmp_buf;
}
方法三:网络字节序,统统转为大端序,网络字节序为大端序,无需判断主机大小端,但是只能转大端序。
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
有朋友可能会这么想,上面我们用一个char类型的buf来接收字节序转换结果。这个数组是在栈里,栈的生长方向有向下生长和向上生长,如果栈向下生长,那么buf[0]的地址不就比buf[1]的地址大了吗?而arm中栈的生长方向也恰恰是向下生长的,这个理解是十分错误的!!!不管栈向上生长也好向下生长也好,buf[0]的地址都是比buf[1]小的,你打印这两个地址也能发现,看图
栈的生长方向向下,数组的生长方向向上,栈的顶部是函数调用压的栈帧,当数组越界时,可能会破坏栈帧内容,里面最终要的LR值,也就是后面pop用的PC值破坏,那函数也就没法返回了,一个segment falut就出现了,或者do page fault就出现了,PC值是个不存在的地址,mmu会报错了,程序凉凉。。。
关于数组生长方向的实验
我们先在栈里定义一个char类型的变量a,然后在定义一个数组,然后通过这个数组的索引来改变这个a的值。
见代码:
#include <stdio.h>
int get_array_value(char *array, int index)
{
return array[index];
}
int main()
{
char a = 10;
char buf[5];
printf("&a:%p, buf:%p, &buf[5]:%p\n", &a, buf, &buf[5]);
printf("sizeof(buf):%d\n", sizeof(buf));
for (iint i = 0; i < sizeof(buf); i++)
{
buf[i] = i;
}
for (int i = 0; i< 1000; i++)
{
printf("a:%d, buf[%d]=%d\n", a, i, get_array_value(buf, i));
if (a == get_array_value(buf, i))
{
buf[i] = 11;
break;
}
}
printf("a after:%d\n", a);
return 0;
}
执行结果
打印出三个变量地址,可以看到a的地址是比数组地址buf多7的,后面实验也是一样buf[7]就是a的值了。我们通过修改buf[7]的值,a的值也随之改变。
变量在栈里的情况:
我们是紧连着分配的a和buf,但是在栈中,他们是没有紧连在一起的,中间还隔着两个字节。a不是buf[5]而是buf[7],这是因为,栈的增长是按4自己对齐的,也就是说sp的增长只能是,sub sp , sp, #4 (#8,#12…)
而a和buf一共占用6个字节,所以sp一次增长8(栈向下生长,表现为sp减少),而a是从栈顶开始分配,所以位置为栈最高的位置,而数组地址可能是对齐的要求,所以分配到了最底端,中间就空了两个字节,实际上如果在a和buf中间再分配两个char,栈地大小也是这样的,刚好填充了中间的两个字节。来看看汇编代码吧,汇编中变量的地址是用[sp #-12]类似的间接寻址的 ,整数赋值用str命令,char型用strb命令。
00010440 <get_array_value>:
10440: e52db004 push {fp} ; (str fp, [sp, #-4]!)
10444: e28db000 add fp, sp, #0
10448: e24dd00c sub sp, sp, #12
1044c: e50b0008 str r0, [fp, #-8]
10450: e50b100c str r1, [fp, #-12]
10454: e51b300c ldr r3, [fp, #-12]
10458: e51b2008 ldr r2, [fp, #-8]
1045c: e0823003 add r3, r2, r3
10460: e5d33000 ldrb r3, [r3]
10464: e1a00003 mov r0, r3
10468: e28bd000 add sp, fp, #0
1046c: e49db004 pop {fp} ; (ldr fp, [sp], #4)
10470: e12fff1e bx lr
00010474 <main>:
10474: e92d4810 push {r4, fp, lr} /*push命令,并不会使sp减少*/
10478: e28db008 add fp, sp, #8
1047c: e24dd014 sub sp, sp, #20 /*分配栈,其中12字节是栈帧(r4,fp,lr), 变量可用大小只有8字节*/
10480: e3a0300a mov r3, #10
10484: e54b3011 strb r3, [fp, #-17] ; 0xffffffef /*a 赋值*/
10488: e3a03000 mov r3, #0
1048c: e50b3010 str r3, [fp, #-16]
10490: e24b3018 sub r3, fp, #24
10494: e2833005 add r3, r3, #5
10498: e24b2018 sub r2, fp, #24
1049c: e24b1011 sub r1, fp, #17
104a0: e3000630 movw r0, #1584 ; 0x630
104a4: e3400001 movt r0, #1
104a8: ebffff8f bl 102ec <printf@plt>
104ac: e3a01005 mov r1, #5
104b0: e300064c movw r0, #1612 ; 0x64c
104b4: e3400001 movt r0, #1
104b8: ebffff8b bl 102ec <printf@plt>
104bc: e3a03000 mov r3, #0
104c0: e50b3010 str r3, [fp, #-16]
104c4: ea000009 b 104f0 <main+0x7c>
104c8: e51b3010 ldr r3, [fp, #-16]
104cc: e6ef1073 uxtb r1, r3
104d0: e24b2018 sub r2, fp, #24
104d4: e51b3010 ldr r3, [fp, #-16]
104d8: e0823003 add r3, r2, r3
104dc: e1a02001 mov r2, r1
104e0: e5c32000 strb r2, [r3]
104e4: e51b3010 ldr r3, [fp, #-16]
104e8: e2833001 add r3, r3, #1
104ec: e50b3010 str r3, [fp, #-16]
104f0: e51b3010 ldr r3, [fp, #-16]
104f4: e3530004 cmp r3, #4
104f8: 9afffff2 bls 104c8 <main+0x54>
104fc: e3a03000 mov r3, #0
10500: e50b3010 str r3, [fp, #-16]
10504: ea00001d b 10580 <main+0x10c>
10508: e55b3011 ldrb r3, [fp, #-17] ; 0xffffffef
1050c: e1a04003 mov r4, r3
10510: e24b3018 sub r3, fp, #24
10514: e51b1010 ldr r1, [fp, #-16]
10518: e1a00003 mov r0, r3
1051c: ebffffc7 bl 10440 <get_array_value>
10520: e1a03000 mov r3, r0
10524: e51b2010 ldr r2, [fp, #-16]
10528: e1a01004 mov r1, r4
1052c: e300065c movw r0, #1628 ; 0x65c
10530: e3400001 movt r0, #1
10534: ebffff6c bl 102ec <printf@plt>
10538: e55b3011 ldrb r3, [fp, #-17] ; 0xffffffef
1053c: e1a04003 mov r4, r3
10540: e24b3018 sub r3, fp, #24
10544: e51b1010 ldr r1, [fp, #-16]
10548: e1a00003 mov r0, r3
1054c: ebffffbb bl 10440 <get_array_value>
10550: e1a03000 mov r3, r0
10554: e1540003 cmp r4, r3
10558: 1a000005 bne 10574 <main+0x100>
1055c: e24b2018 sub r2, fp, #24
10560: e51b3010 ldr r3, [fp, #-16]
10564: e0823003 add r3, r2, r3
10568: e3a0200b mov r2, #11
1056c: e5c32000 strb r2, [r3]
10570: ea000005 b 1058c <main+0x118>
10574: e51b3010 ldr r3, [fp, #-16]
10578: e2833001 add r3, r3, #1
1057c: e50b3010 str r3, [fp, #-16]
10580: e51b3010 ldr r3, [fp, #-16]
10584: e3530ffa cmp r3, #1000 ; 0x3e8
10588: baffffde blt 10508 <main+0x94>
1058c: e51b1010 ldr r1, [fp, #-16]
10590: e3000670 movw r0, #1648 ; 0x670
10594: e3400001 movt r0, #1
10598: ebffff53 bl 102ec <printf@plt>
1059c: e55b3011 ldrb r3, [fp, #-17] ; 0xffffffef
105a0: e1a01003 mov r1, r3
105a4: e3000678 movw r0, #1656 ; 0x678
105a8: e3400001 movt r0, #1
105ac: ebffff4e bl 102ec <printf@plt>
105b0: e3a03000 mov r3, #0
105b4: e1a00003 mov r0, r3
105b8: e24bd008 sub sp, fp, #8
105bc: e8bd8810 pop {r4, fp, pc}
tips:push,pop命令并不会使sp改变,需要我们手动改变sp的值