• 面试经典大端小端实践应用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 // 恢复栈地址
    
  • 相关阅读:
    URL和DNS解析
    web工作方式,浏览网页,打开浏览器,输入网址按下回车键,然后会显示出内容,这个过程是怎样的呢?
    PHP根据数组的值分组
    EditPlus注册码在线生成,强大
    php获取内容中第一张图片地址
    PHP函数ip2long转换IP时数值太大产生负数的解决办法
    js Uncaught SyntaxError: Unexpected token错误
    虚拟机centos6.5 --VirtualBox设置全屏
    虚拟机centos6.5 --设置静态ip
    centos之开放80端口
  • 原文地址:https://www.cnblogs.com/obama/p/3027593.html
Copyright © 2020-2023  润新知