面试经典--大端小端--实践应用1

我在前面总结过大端小端的基本概念,一句话说就是对于变量的二进制表示,如果低地址存放的是二进制位的高位,那么说明CPU是大端模式,反之则为小端模式。的确有了这句话的总结之后很容易记忆,我也自以为得其真谛,但是在接下来看题目,做笔试题的过程中发现对于大端小端的理解,还得结合其它的知识点来看。


首先就是栈生长的方向问题,栈生长的方向是由高地址向低地址生长,也就是说栈底的地址高一些,而堆则相反,下面结合一个具体的例子来说明,请看如下代码:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
	int a=0x9abc0e0d;
	int b=0x12345678;
	
	char *p=(char*)&b;
	char *q=p+2;

	int x=*q;
	int y=*(int *)q;
	printf("&a=%x,&b=%x\n",&a,&b );
	printf("x=%x,y=%x\n",x,y );

	int *pc=(int*)malloc(sizeof(int));
	int *pd=(int*)malloc(sizeof(int));

	*pc=0x12345678;
	*pd=0x9abc0e0d;

	char *c=(char*)pc;
	char *d=c+2;
	int m=*d;
	int n=*(int*)d;

 	printf("pc=%x,pd=%x\n",pc,pd );
 	printf("m=%x,n=%x\n",m,n );

	return 0;
}
输出如下:
image


现在对结果进行分析,首先,很清楚地可以看到栈地址是从高往低生长的,一般而言,在debug模式下面,生长不一定连续,会有一些无用字符填充,但在release模式下一般是连续的。

而在我用gcc编译运行得出的结果中我们可以看到貌似是连续的。而堆的结果说明堆是由低到高生长的,而且是不连续的。
现在分析大端小端问题。对于a,b连续存放的这种情况,如果CPU是小端模式,正如我手头的x86机器,我们可以画出栈中数据如下:
 
低地址image高地址
 
x表示q指向的一个字符,于是其值就为0x34,y表示q指向的整数,自然就会被解析为0xe0d1234.

其次是参数传递的问题,腾讯笔试中出现了这么一道题目,代码如下:
#include <cstdio>
int main(int argc, char **argv)
{
	long long a = 1;
	long long b = 2;
	long long c = 3;

	printf("%d,%d,%d", a, b, c);
}

 

求输出多少?

首先需要明确的是printf只是解释所指的内存单元里面有些啥,不会强制类型转换,其次long long 类型是64位的,但是我就是傻掉了,以为会强制转换,加上自己不知道long long 类型是个什么类型,所以只好认为会强制转换(虽然潜意识里面第一眼就觉得B:102是答案,最后还是没忍住,改了答案)。然后需要明确的是默认参数入栈顺序是从右至左。有了这个之后我们很容易就能获得栈中的状态:

低地址(栈顶) %d%d%d
1 0
2 0
3 0
高地址(栈底)  

因此可以很清楚地看到答案为102.下面给出另一位仁兄(文章链接)反汇编得出的代码:

// 从开始执行main函数体内的第一条赋值语句(long long a=1;)开始。

PUSH EBP     // 保存ebp
 MOV EBP,ESP // 设置ebp
 SUB ESP,0F0 // 分配栈空间
 PUSH EBX    // 保护寄存器 ebx,esi,edi
 PUSH ESI
 PUSH EDI
 LEA EDI,DWORD PTR SS:[EBP-F0] // 将分配的栈空间赋初值,初始化为CCCCCCCC
 MOV ECX,3C                    // ecx用作下面rep stos指令的计数器
 MOV EAX,CCCCCCCC
 REP STOS DWORD PTR ES:[EDI]
 MOV DWORD PTR SS:[EBP-C],1   // long long a=1;
 MOV DWORD PTR SS:[EBP-8],0
 MOV DWORD PTR SS:[EBP-1C],2  // long long b=2;
 MOV DWORD PTR SS:[EBP-18],0
 MOV DWORD PTR SS:[EBP-2C],3  // long long c=3;
 MOV DWORD PTR SS:[EBP-28],0
 MOV ESI,ESP
 MOV EAX,DWORD PTR SS:[EBP-28] // 参数c入栈
 PUSH EAX
 MOV ECX,DWORD PTR SS:[EBP-2C]
 PUSH ECX
 MOV EDX,DWORD PTR SS:[EBP-18] // 参数b入栈
 PUSH EDX
 MOV EAX,DWORD PTR SS:[EBP-1C]
 PUSH EAX
 MOV ECX,DWORD PTR SS:[EBP-8]  // 参数a入栈
 PUSH ECX
 MOV EDX,DWORD PTR SS:[EBP-C]
 PUSH EDX
 PUSH test4.0041573C // “%d,%d,%d”入栈(0041573C地址开始的位置存放“%d,%d,%d”)
 CALL printf // 开始调用printf函数
 ADD ESP,1C // 恢复栈地址
posted @ 2013-04-18 01:13  曾见绝美的阳光  阅读(387)  评论(0编辑  收藏  举报