面试经典--大端小端--实践应用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 // 恢复栈地址

你可能感兴趣的:(面试)