精华内容
下载资源
问答
  • 内联汇编

    2020-06-30 22:07:57
    什么是内联汇编 内联汇编是指在 C/C++ 代码中嵌入的汇编代码,与全部是汇编的汇编源文件不同,它们被嵌入到 C/C++ 的大环境中。内联汇编方式两个作用,一是程序的某些关键代码直接用汇编语言编写,可提高代码的执行...

    什么是内联汇编

    内联汇编是指在 C/C++ 代码中嵌入的汇编代码,与全部是汇编的汇编源文件不同,它们被嵌入到 C/C++ 的大环境中。内联汇编方式两个作用,一是程序的某些关键代码直接用汇编语言编写,可提高代码的执行效率;二是有些操作无法通过高级语言实现,或者实现起来很困难,必须借助汇编语言达到目的。

    x86 内联汇编

    使用内联汇编要用到 __asm 关键字,它可以出现在任何允许 C/C++ 语句出现的地方。对 __asm 关键字的使用有两种方式:

    // 1.
    __asm
     {
       // 汇编代码
     }
    
    // 2.
    __asm // 汇编代码
    

    显然,第一种方法与 C/C++ 的风格很一致,并且把汇编代码和 C/C++ 代码清楚地分开,还避免了重复输入 __asm 关键字,因此推荐使用第一种方法。

    不像在 C/C++ 中的“{ }”,__asm 块的“{ }”不会影响 C/C++ 变量的作用范围。同时,__asm 块可以嵌套,而且嵌套也不会影响变量的作用范围。

    为了与低版本的 Visual C++ 兼容,_asm 和 __asm 具有相同的意义。另外,Visual C++ 支持标准 C++ 的 asm 关键字,但是它不会生成任何指令,它的作用仅限于使编译器不会出现编译错误。要使用内联汇编,必须使用 __asm 而不是 asm 关键字。

    例子:

    void MyFunc(char *pszText)
    {
        printf("%s\n", pszText);
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
        char str1[] = "__asm{ }";
        char str2[] = "__asm";
        
        // 32位程序内联汇编 第一种方式
        __asm
        {
            	lea       eax, str1
                push      eax
                mov       eax, MyFunc
                call      eax
        }
        
        // 32位程序内联汇编 第二种方式
        __asm lea     eax, str2
        __asm push    eax
        __asm mov     eax, MyFunc
        __asm call    eax
    
        system("pause");
        return 0;
    }
    

    效果图:
    在这里插入图片描述


    x64 内联汇编

    在 64 位程序中,已经不能使用关键字 __asm 来添加汇编代码,而应把汇编代码全部写在 .asm 文件中,然后,再将 .asm 包含到项目中编译链接。现在,我们就先来讲解如何使用 VS2013 添加并编译 .asm 文件的步骤。

    首先,我们在本地上新建一个 .asm 格式的文件 “myasm.asm”之后,右击项目工程并选择“添加” —> “现有项”,然后选择我们新创建的“myasm.asm”文件,添加到工程中:
    在这里插入图片描述

    然后,我们选中“myasm.asm”文件,并右击选择“属性”:
    在这里插入图片描述
    在“myasm.asm属性页”中,设置 从生成中排除 为“否”,设置 项类型 为“自定义生成工具”,然后,点击“应用”。这时,在窗口左侧就会生成“自定义生成工具”的扩展栏。如果是从 x64 模式下设置的,在一步,会没有反应或者卡死。所以,一定要从 Win32 模式开始,再创建 x64 模式,并把 Win32 的设置复制到 x64 模式中,便可以解决这个问题。

    在这里插入图片描述
    接着,我们开始新建 x64 模式,因为我们要开发的是 64 位程序。我们选中项目工程,以此选择 “属性” —> “配置属性” —> “配置管理器” —> “活动解决方案平台”选择“新建”。这时,就会来到“新建解决方案平台”页面。我们选择“x64”,并从 Win32 中复制设置,创建新的项目平台,点击“确定”。这时,就可以使用 x64 模式编译 64 位程序了。
    在这里插入图片描述

    然后,我们继续对 .asm 文件进行设置,将其包含到项目工程中来编译链接。选中“myasm.asm”文件,右击选择“属性”,来到“myasm.asm”属性页进行设置。在 命令行 中输入“ml64 /c %(fileName).asm”,在 输出 中输入“%(fileName).obj”,其它保持默认即可,点击“确定”即可完成设置。

    在这里插入图片描述

    经过上述几个步骤,我们成功为 x64 程序添加 .asm 文件并设置包含到项目工程中编译链接。接下来,我们就开始讲解如何在 .asm 文件中写汇编代码了。


    对于 64 位程序在 .asm 中写代码,需要遵循以下几个规则:
    会变文件 .asm 文件必须以关键字 .CODE 开始,关键字 END 结束,大小写都可以。

    .code
        ; 此处写汇编指令代码
    end
    

    所有的汇编代码以函数方式组织在一起。也就是说,我们要将汇编代码封装成一个个汇编函数。要注意 64 位汇编中的函数声明以及调用约定:

    .code
    ; _MyAdd是汇编函数
    _MyAdd    proc
        ; 此处写汇编函数的代码
    _MyAdd    endp
    end
    

    其中, _MyAsm 是汇编函数的名称,proc 是汇编函数的关键字,endp 是汇编函数的结尾关键字。

    要注意和 32 位汇编函数的区别:32 位汇编函数调用约定 __stdcall,所有参数从右到左依次入栈,通过压栈传递参数。64 位汇编函数的调用约定 __fastcall,前 4 个参数是从左至右依次存放于RCX、RDX、R8、R9寄存器里面,剩下的参数从左至右顺序入栈。

    代码:
    (myasm.asm)

    .code
    _MyAdd    proc
        xor        rax, rax
        mov        rax, rcx
        add        rax, rdx
        add        rax, r8
        add        rax, r9
        ret
    _MyAdd    endp
    end
    

    (Test.cpp)

    extern "C" ULONGLONG _MyAdd(ULONGLONG a1, ULONGLONG a2, ULONGLONG a3, ULONGLONG a4);
    int _tmain(int argc, _TCHAR* argv[])
    {
        ULONGLONG a1 = 1;
        ULONGLONG a2 = 2;
        ULONGLONG a3 = 3;
        ULONGLONG a4 = 4;
        ULONGLONG b = _MyAdd(a1, a2, a3, a4);
        printf("b=%d\n", b);
        system("pause");
        return 0;
    }
    

    效果图:
    在这里插入图片描述

    vs2019 x64 内敛汇编

    打开生成依赖项 —— 生成自定义
    在这里插入图片描述

    勾选 masm :
    在这里插入图片描述

    在创建的 asm 文件的属性里选择:
    在这里插入图片描述

    展开全文
  • VS2019 内联汇编开发

    千次阅读 2020-11-21 21:29:12
    内联汇编是在C,C++代码内部嵌入一部分汇编代码, 这部分代码会被编译器跳过直接拼接. 为什么要用内联汇编? 这种情况一般是由于我们对于当前的编译器的能力感到不满意, 所以需要代替编译器来优化一些代码片段. 当然...

    前言

     

    首先什么是内联汇编?

    内联汇编是在C,C++代码内部嵌入一部分汇编代码, 这部分代码会被编译器跳过直接拼接.

    为什么要用内联汇编?

    这种情况一般是由于我们对于当前的编译器的能力感到不满意, 所以需要代替编译器来优化一些代码片段. 当然我们可以完全进行汇编实现, 直接把函数写成一个.asm汇编文件(这个文件可以用yasm, nasm, masm进行编译为object文件参与代码链接). 内联汇编相比较而言不用实现整个函数, 比如函数的入栈和出栈的操作, 栈指针也不需要你去计算然后移动, 只需要用寄存器实现高效率的计算.

    内联汇编需要特殊的编译器或者命令吗?

    内联汇编属于正常C++编译器的特性, 不需要额外的编译命令. 但是MSVC目前只支持32位的内联汇编, 64位汇编据官方说法是没必要.  如果使用Clang进行编译的话就没有问题, 都支持.

    微软原话:Inline assembly is not supported on the ARM and x64 processors.  具体官方链接在这里:https://docs.microsoft.com/zh-cn/cpp/assembler/inline/inline-assembler?view=msvc-160

     

    正文

    一. 32位汇编

    a. 首先创建一个win32的空项目, 这一步省略, 可以参考我前面的文章.

    b. 一个简单的内联汇编代码如下:

    #include <stdio.h>
    
    int main()
    {
      int test = 1;
      __asm
      {
        mov  eax, test //把test的值写入eax寄存器
        dec  eax  //寄存器数值减一
        mov  test, eax //把eax的值写回test变量中
      }
      printf("test:%d\n", test); //test 为0
      return 1;
    }

    c. 整体看起来是这样的:

    可以看到写32位的内联汇编只需要修改代码, 不需要更改VS的任何配置就可以.

    补充一个内联汇编的技巧:

    操作数组和_asm行代码:

    __asm后面可以直接跟一行汇编代码. 另外汇编代码支持[]的索引操作符.

    但是, 这里有个容易犯错的地方:

    这个易错点就是[]里面的index是字节offset, 不是元素的index. 从上图中可以看到mov arr[1], 2; 这个代码实际上把2写到了arr的第0个元素arr[0]的第二个字节上了, 也就是增加上了512, 加上它自己的2,所以元素0是514.

    正确的写法是:

     

    二. 64位汇编

    相比较于32位汇编, 64位要麻烦一点, 因为VS原生不支持64位汇编,所以这里需要使用clang来帮助编译.

    首先把刚刚这段代码直接用VS的x64配置编译:

    会有如下错误:

    说的很清楚, 该架构不支持内联汇编__asm关键字.

    解决方法如下:

    1. 在VS2019中使用LLVM Clang作为工具集:

    成功后如下:

    你有可能没有安装Clang工具集. 打开Visual Studio Installer:

    这个安装流程已经提过很多次了, 所以这里就只是示范图片.

    安装好了以后, 注释掉clang不支持的数组操作,可以直接编译运行如下:

    32位 64位内联汇编在clang下都没有任何问题.

    2. 使用custom build, 使用clang单独编译64位内联汇编

    如果你的项目比较小, 那第一种方法完全可以解决你的问题. 但是, 如果你的项目需要和别的VS项目一起协同, 整个项目使用Clang作为工具集并不是一种很保险的操作.

    这种情况下, 我们单独将内联汇编代码集中到一个cpp中, 将这个cpp单独使用clang来进行编译. 这样的话, 项目整体还是作为VS项目. 这种单独用clang编译某些源码的操作就是custom build, 客户自定义生成.

    首先你需要安装好llvm的clang编译器, 打开控制台cmd, 输入clang --version:

    如果正确显示则没有问题. 如果没有安装的话,请看我上一篇文章安卓控制台的开发中有写到.

    a. 先创建一个custom.cpp来写内联汇编函数.

    
    int asminc(int a)
    {
      asm(
        "inc %[_a] \n" //_a是下面指定的符号名对应传入变量int a; 
                       //\n是表示换行,如果没有这个换行符实际上等于 inc%[_a] inc%0
        "inc %0 \n"  //%0表示第0个传入参数和上一句的意义实际上是一样的,都表示 int a
        :[_a]"+r"(a) //这里表示输出列表,
                     //_a表示asm代码里面的符号名,"+r"表示会修改a的值,(a)中的a表示int a映射到asm代码块中
        :           //这里表示输入列表, 如果只是读取值的变量就写到这里
        :           //这里是寄存器保留列表, 如果加上eax的话, 编译器会在运行汇编之前保证eax寄存器是可用的, 否则
                    //eax可能还保留着上面的代码需要的数据
      );
      return a;
    
    }

     b. 增加custom build命令行:

    右键InlineASM项目,点击属性打开项目属性页,增加自定义生成工具命令行:

    其中 32位的命令行是:

    clang-cl -c %(Filename).cpp --target=i386 -o $(OutputPath)%(Filename).obj

    64位的命令行是:

    clang-cl -c %(Filename).cpp --target=x86_64 -o $(OutputPath)%(Filename).obj

    clang是跨平台的编译器, 主要根据target来决定将cpp编译成什么系统架构, 编译选项如下:

    Target Triple

    The basic option is to define the target architecture. For that, use -target <triple>. If you don’t specify the target, CPU names won’t match (since Clang assumes the host triple), and the compilation will go ahead, creating code for the host platform, which will break later on when assembling or linking.

    The triple has the general format <arch><sub>-<vendor>-<sys>-<abi>, where:

    • arch = x86_64i386armthumbmips, etc.
    • sub = for ex. on ARM: v5v6mv7av7m, etc.
    • vendor = pcapplenvidiaibm, etc.
    • sys = nonelinuxwin32darwincuda, etc.
    • abi = eabignuandroidmachoelf, etc

    clang-cl是Windows下的clang工具名称, cl是命令行风格的意思, 当然用clang也是可以的, 只是有些时候会提醒你使用clang-cl代替clang.

    如果有任何的编译选项的问题,比如头文件路径问题啊,指令集的问题, 可以参考这个文章:https://clang.llvm.org/docs/UsersManual.html#c-ms

     

    修改cpp文件的类型为自定义生成工具:

    点击自定义生成工具,然后点击应用, 这时候左边会出现自定义生成工具的页标 .点击自定义工具下的常规, 查看命令行和输出是否正确, 默认状态下是和上面的clang命令一致的.

    但是如果你不小心修改了, 可以选择继承父项目恢复它:

     c. 修改main.cpp中的调用, 运行:

    asm函数内部对传入参数加了两次1, 输出为3, 正确.

    c. 再加上刚刚的dec的函数:

    注意看, 这两种内联汇编代码风格是完全不一样的. asmdec中是intel风格, 而 asminc中是属于AT&T风格. 这两种风格都支持, 各有好坏.

    AT&T语法参考https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

    intel语法参考最开始微软的文章: https://docs.microsoft.com/zh-cn/cpp/assembler/inline/inline-assembler?view=msvc-160

    64位下两种语法都没问题:

    32位下会有这个编译问题:

    error : expected '(' after 'asm'

    也就是32位下不支持这种Intel语法, 由于32位汇编和64位汇编基本上是不一样的. 所以不存在一套汇编代码32位,64位共用的情况, 32位下可以不用custom build直接使用MSVC进行编译就行.

    又想了一下, 刚开始的LLVM Clang作为工具集在x86的配置下明显是支持Intel语法的, 所以我这里的clang 命令行肯定有些关于Windows或者微软的某些传入参数没有打开导致Intel语法不能使用.

    这个也不是很紧要, 如果以后我发现了再更新到这篇文章.

     

    总结

    32位的内联汇编:

    MSVC支持 Intel语法

    Clang支持 Intel和AT&T语法

    64位的内联汇编:

    MSVC不支持:

    Clang支持 Intel和AT&T语法

    另外, 虽然上文全部展示的是x86的汇编, 但是实际上Armeabi和Arm64的汇编都可以通过Clang用AT&T语法来做, intel语法不支持Arm. 

    所以我建议用clang来进行内联汇编的编译, 另外统一使用AT&T语法(汇编代码肯定每个架构一套, 统一的意思是指同一种内联风格).

     

    最后

    内联汇编能有哪些提升,:

    1. 调节某些指令的顺序使CPU指令的pipeline的利用率最大化

    2. 增加寄存器数据的利用率, 避免从栈内存加载和保存临时数据

    其中1的提升是比较小的, 这部分编译器其实做的很不错了.

    相比较而言, 2的提升可以很大. 这种提升主要是在代码块需要进行大量数据的load参与计算,计算完成后再把计算的数据store到内存地址上,

    其中可能有很多的中间计算结果. 如果编译器没有分配好寄存器资源, 可能导致中间计算结果会在计算过程中被临时保存到栈内存的变量地址中,

    下次用的时候再从内存里加载回来, 这种操作对代码的运行来说是非常影响效率的.

    经验来看的话, 经过深度优化的汇编代码应该可以比编译器O2优化等级的代码快上20%以上.

     

     

     

     

     

     

     

    展开全文
  • 使用内联汇编

    2011-07-13 23:55:53
    一、内联汇编的优缺点 因为在Visual C++中使用内联汇编不需要额外的编译器和联接器,且可以处理Visual C++中不能处理的一些事情,而且可以使用在C/C++中的变量,所以非常方便。内联汇编主要用于如下场合: 1.使用汇编...

    一、内联汇编的优缺点

      因为在Visual C++中使用内联汇编不需要额外的编译器和联接器,且可以处理Visual C++中不能处理的一些事情,而且可以使用在C/C++中的变量,所以非常方便。内联汇编主要用于如下场合:

      1.使用汇编语言写函数;
      2.对速度要求非常高的代码;
      3.设备驱动程序中直接访问硬件;
      4."Naked" Call的初始化和结束代码。 (_declspec(naked))

      //(."Naked",不需要C/C++的编译器(自作聪明)生成的函数初始化和收尾代码,请参看MSDN的"Naked Functions"的说明)

      内联汇编代码不易于移植,如果你的程序打算在不同类型的机器(比如x86和Alpha)上运行,应当尽量避免使用内联汇编。这时候你可以使用MASM,因为MASM支持更方便的的宏指令和数据指示符。

      二、内联汇编关键字

      在Visual C++使用内联汇编用到的是__asm关键字,这个关键字有两种使用方法:

      1.简单__asm块

    __asm
    {
    MOV AL, 2
    MOV DX, 0XD007
    OUT AL, DX
    }


      2.在每条汇编指令之前加__asm关键字

    __asm MOV AL, 2
    __asm MOV DX, 0xD007
    __asm OUT AL, DX


      因为__asm关键字是语句分隔符,因此你可以把汇编指令放在同一行:

      __asm MOV AL, 2 __asm MOV DX, 0XD007 __asm OUT AL, DX

      显然,第一种方法和C/C++的风格很一致,并且有很多其它优点,因此推荐使用第一种方法。

      不象在C/C++中的"{}",__asm块的"{}"不会影响C/C++变量的作用范围。同时,__asm块可以嵌套,嵌套也不会影响变量的作用范围。

      三、在__asm块中使用汇编语言

      1.内联汇编指令集

      内联汇编完全支持的Intel 486指令集,允许使用MMX指令。不支持的指令可以使用_EMIT伪指令定义(_EMIT伪指令说明见下文)。

      2.MASM表达式

      内联汇编可以使用MASM中的表达式。比如: MOV EAX, 1。

      3.数据指示符和操作符

      虽然__asm块中允许使用C/C++的数据类型和对象,但它不能用MASM指示符和操作符定义数据对象。这里特别指出,__asm块中不允许MASM中的定义指示符:DB、DW、DD、DQ、DT和DF,也不允许DUP和THIS操作符。MASM结构和记录也不再有效,内联汇编不接受STRUC、RECORD、WIDTH或者MASK。

      4.EVEN和ALIGN指示符

      尽管内联汇编不支持大多数MASM指示符,但它支持EVEN和ALIGN,当需要的时候,这些指示符在汇编代码里面加入NOP(空操作)指令使标号对齐到特定边界。这样可以使某些处理器取指令时具有更高的效率。

      5.MASM宏指示符

      内联汇编不是宏汇编,不能使用MASM宏指示符(MACRO、REPT、IRC、IRP和ENDM)和宏操作符(<>、!、&、%和.TYPE)。

      6.段说明

      必须使用寄存器来说明段,跨越段必须显式地说明,如ES:[BX]。

      7.类型和变量大小

      我们可以使用LENGTH来取得C/C++中的数组中的元素个数,如果不是一个数组,则结果为一。使用SIZE来取得C/C++中变量的大小,一个变量的大小是LENGTH和TYPE的乘积。TYPE用来取得一个变量的大小,如果是一个数组,它得到的一个数组中的单个元素的大小。

      8.注释

      可以使用C/C++的注释,但推荐用ASM的注释,即";"号。

      9._EMIT伪指令

      _EMIT伪指令相当于MASM中的DB,但一次只能定义一个字节,比如:

    __asm
    {
    JMP _CodeOfAsm

    _EMIT 0x00 ; 定义混合在代码段的数据
    _EMIT 0x01

    _CodeOfAsm:
    ; 这里是代码
    _EMIT 0x90 ; NOP指令
    }

      四、在__asm块中使用C/C++语言元素

      C/C++与汇编可以混合使用,在内联汇编可以使用C/C++的变量和很多其它C/C++的元素。在__asm块中可以使用以下C/C++元素:

      1.符号,包括标号、变量和函数名;

      2.常量,包括符号常量和枚举型(enum)成员;

      3.宏定义和预处理指示符;

      4.注释,包括"/**/"和"//";

      5.类型名,包括所有MASM中合法的类型

      6.typedef名称, 像PTR、TYPE、特定的结构成员或枚举成员这样的通用操作符。

      在__asm块中,可以使用C/C++或ASM的基数计数法(比如: 0x100和100H是相等的)。

      __asm块中不能使用像<<一类的C/C++操作符。C/C++和MASM通用的操作符,比如"*"和"[]"操作符,都被认为是汇编语言的操作符。举个例子:

    int array[10];

    __asm MOV array[6], BX ; Store BX at array+6 (not scaled)

    array[6] = 0; /* Store 0 at array+12 (scaled) */

      * 小技巧: 内联汇编中,你可以使用TYPE操作符使作其与C一致。比如,下面两条语句是一样的:

    __asm MOV array[6 * TYPE int], 0 ; Store 0 at array + 12

    array[6] = 0; /* Store 0 at array + 12 */

      内联汇编能通过变两名直接引用C/C++的变量。__asm块中可以引用任何符号,包括变量名。

      如果C/C++中的类、结构或者枚举成员具有唯一的名称,如果在"."操作符之前不指定变量或者typedef名称,则__asm块中只能引用成员名称。然而,如果成员不是唯一的,你必须在"."操作符之前加上变量名或typedef名称。例如,下面的两个结构都具有same_name这个成员变量:

    struct first_type
    {
    char *weasel;
    int same_name;
    };

    struct second_type
    {
    int wonton;
    long same_name;
    };

      如果按下面声明变量:

    struct first_type hal;
    struct second_type oat;

      那么,所有引用same_name成员的地方都必须使用变量名,因为same_name不是唯一的。另外,上面的weasel变量具有唯一的名称,你可以仅仅使用它的成员名称来引用它:

    __asm
    {
    MOV EBX, OFFSET hal
    MOV ECX, [EBX]hal.same_name ; 必须使用 'hal'
    MOV ESI, [EBX].weasel ; 可以省略 'hal'
    }

      注意,省略了变量名仅仅是为了写代码的方便,生成的汇编指令的还是一样的。

      可以不受限制地访问C++成员变量,但是不能调用C++的成员函数。

       五、寄存器使用

      一般来说,在__asm块开始的时候,寄存器是空的,不能在两个__asm之间保存寄存器的值。(这是MSDN上说的,我在实际使用时发现,好像并不是这样。不过它是说"一般",我是特殊:))

      如果一个函数被声明成了__fastcall,则其参数将放在寄存器中,这将给寄存器的管理带来问题。所以,如果要将一个函数声明成__fastcall,必须保存ECX寄存器。为了避免以上的冲突,在声明为__fastcall的函数中不要有__asm块。如果用了/Gr编译选项(它全局的变成__fastcall),将每个函数声明成__cdecl或者__stdcall,这个属性告诉编译器用传统的C方法。

      如果使用EAX、EBX、ECX、EDX、ESI和EDI寄存器,你不需要保存它;但如果你用到了DS、 SS、SP、BP和标志寄存器,那就应该PUSH保存这些寄存器。

      如果程序中改变了用于STD和CLD的方向标志,你必须将其恢复到原来的值。

       六、转跳

      可以在C里面使用goto调到__asm块中的标号处,也可以在__asm块中转跳到__asm块里面和外面的标号处。__asm块内的标号是不区分大小写的(指令、指示符等也是不区分大小写的)。例:

    void func()
    {
    goto C_Dest; /* 合法 */
    goto c_dest; /* 错误 */

    goto A_Dest; /* 合法 */
    goto a_dest; /* 合法 */

    __asm
    {
    JMP C_Dest ; 合法
    JMP c_dest ; MSDN上说合法,但是我在VS.NET中编译,认为这样不合法

    JMP A_Dest ; 合法
    JMP a_dest ; 合法

    a_dest: ; __asm 标号
    }

    C_Dest: /* C的标号 */
    return;
    }

      不要使用函数名称当作标号,否则将使其跳到函数执行而不是标号处。如下所示:

    ; 错误: 使用函数名作为标号
    JNE exit
    .
    .
    .
    exit:
    ; 下面是更多的ASM代码

      美元符号$用于指定当前位置,如下所用,常用于条件跳转:

    JNE $+5 ; 下面这条指令的长度是5个字节
    JMP farlabel
    ;$+5,跳到了这里
    .
    .
    .
    farlabel:
    七、调用函数

      内联汇编调用C/C++函数必须自己清除堆栈,下面是一个调用C/C++函数例子:

    #include

    char szformat[] = "%s %s/n";
    char szHello[] = "Hello";
    char szWorld[] = " world";
    void main()
    {
    __asm
    {
    MOV EAX, OFFSET szWorld
    PUSH EAX
    MOV EAX, OFFSET szHello
    PUSH EAX
    MOV EAX, OFFSET szformat
    PUSH EAX
    CALL printf

    //内联汇编调用C函数必须自己清除堆栈
    //用不使用的EBX寄存器清除堆栈,或ADD ESP, 12
    POP EBX
    POP EBX
    POP EBX
    }
    }


      注意:函数参数是从右向左压栈。

      不能够访问C++中的类成员函数,但是可以访问extern "C"函数。

      如果调用Windows API函数,则不需要自己清除堆栈,因为API的返回指令是RET n,会自动清除堆栈

      比如下面的例子:

    #include

    char szAppName[] = "API Test";

    void main()
    {
    char szHello[] = "Hello, world!";

    __asm
    {
    PUSH MB_OK OR MB_ICONINformATION
    PUSH OFFSET szAppName ; 全局变量用OFFSET
    LEA EAX, szHello ; 局部变量用LEA
    PUSH EAX
    PUSH 0
    CALL DWORD PTR [MessageBoxA] ; 注意这里,我费了好大周折才发现不是CALL MessageBoxA
    }
    }


      一般来说,在Visual C++中使用内联汇编是为了提高速度,因此这些函数调用尽可能用C/C++写。

      八、一个例子

      下面的例子是在VS.NET(即VC7)中C语言写的。先建一个工程,将下列代码放到工程中的.c文件中编译,无需作特别的设置,即可编译通过。


    //预处理
    #include
    ///

    ///
    //全局变量
    HWND g_hWnd;
    HINSTANCE g_hInst;

    TCHAR szTemp[1024];

    TCHAR szAppName[] = "CRC32 Sample";
    /

    /
    //函数声明
    DWORD GetCRC32(const BYTE *pbData, int nSize);
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow);
    LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    /

    /
    //主函数
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
    {
    MSG msg;
    WNDCLASSEX wndClassEx;

    g_hInst = hInstance;

    wndClassEx.cbSize = sizeof(WNDCLASSEX);
    wndClassEx.style = CS_VREDRAW | CS_HREDRAW;
    wndClassEx.lpfnWndProc = (WNDPROC) WindowProc;
    wndClassEx.cbClsExtra = 0;
    wndClassEx.cbWndExtra = 0;
    wndClassEx.hInstance = g_hInst;
    wndClassEx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndClassEx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndClassEx.hbrBackground = (HBRUSH) (COLOR_WINDOW);
    wndClassEx.lpszMenuName = NULL;
    wndClassEx.lpszClassName = szAppName;
    wndClassEx.hIconSm = NULL;

    RegisterClassEx(&wndClassEx);

    g_hWnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPED |WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX,
    CW_USEDEFAULT, CW_USEDEFAULT, 300, 70,
    NULL, NULL, g_hInst, NULL);

    ShowWindow(g_hWnd, iCmdShow);
    UpdateWindow(g_hWnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }
    return ((int) msg.wParam);
    }
    ///

    //
    //主窗口回调函数
    LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
    switch (uMsg)
    {
    case WM_CREATE:
    CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE |WS_BORDER | ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_NOHIDESEL |WS_OVERLAPPED,
    7, 12, 220, 22,
    hWnd, (HMENU)1000, g_hInst, NULL);
    CreateWindowEx(0, "BUTTON", "&OK", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_OVERLAPPED | BS_FLAT,
    244, 12, 40, 20,
    hWnd, (HMENU)IDOK, g_hInst, NULL);

    break;

    case WM_COMMAND:
    switch (LOWORD(wParam))
    {
    case IDOK:
    GetDlgItemText(g_hWnd, 1000, szTemp + 100, 800);
    wsprintf(szTemp, "当前文本框内的字符串的CRC32校验码是: 0x%lX", GetCRC32(szTemp + 100, (int)strlen(szTemp + 100)));
    MessageBox(g_hWnd, szTemp, szAppName, MB_OK|MB_ICONINformATION);
    }
    break;

    case WM_DESTROY:
    PostQuitMessage(0);
    break;

    default:
    return (DefWindowProc(hWnd, uMsg, wParam, lParam));
    }
    return (0);
    }
    /

    /
    //GetCRC32: 求字节流的CRC32校验码
    //参数:
    // pbData: 指向字节流缓冲区首地址
    // nSize: 字节流长度
    //
    //返回值:
    // 字节流的CRC32校验码
    //
    //这里使用查表法求CRC32校验码,这部分是参考老罗的文章《 矛与盾的较量(2)——CRC原理篇》该写的。
    //原文的具体内容请参看: http://asp.7i24.com/netcool/laoluo/articles/show_article.asp?Article_ID=15
    //
    //下面使用内联汇编求CRC32校验码,充分使用了CPU中的寄存器,速度和方便性都是使用C/C++所不能比拟的
    //
    DWORD GetCRC32(const BYTE *pbData, int nSize)
    {
    DWORD dwCRC32Table[256];

    __asm //这片内联汇编是初始化CRC32表
    {
    MOV ECX, 256

    _NextTable:
    LEA EAX, [ECX-1]
    PUSH ECX
    MOV ECX, 8

    _NextBit:
    SHR EAX, 1
    JNC _NotCarry
    XOR EAX, 0xEDB88320
    _NotCarry:
    DEC ECX
    JNZ _NextBit

    POP ECX
    MOV [dwCRC32Table + ECX*4 - 4], EAX
    DEC ECX
    JNZ _NextTable
    }

    __asm //下面是求CRC32校验码
    {
    MOV EAX, -1
    MOV EBX, pbData
    OR EBX, EBX
    JZ _Done
    MOV ECX, nSize
    OR ECX, ECX
    JZ _Done

    _NextByte:
    MOV DL, [EBX]

    XOR DL, AL
    MOVZX EDX, DL
    SHR EAX, 8
    XOR EAX, [dwCRC32Table + EDX*4]

    INC EBX
    LOOP _NextByte
    _Done:
    NOT EAX
    }
    }


    展开全文
  • 内联汇编基础知识

    千次阅读 2006-02-26 00:04:00
    内联汇编基础知识 一、 内联汇编简述 Visual C++ 6.0编译器下,内联汇编可以使用所有的Intel486处理器指令集。而且可以对目标处理器建立起伪指令来实现附加指令功能。内联汇编可以使用MASM编译器所允许的表达式,...

    内联汇编基础知识
     

    一、 内联汇编简述

      Visual C++ 6.0编译器下,内联汇编可以使用所有的Intel486处理器指令集。而且可以对目标处理器建立起伪指令来实现附加指令功能。内联汇编可以使用MASM编译器所允许的表达式,其中的一些表达式可以通过操作符和操作数的组合,对单精值进行运算。

      虽然内联汇编可以访问C/C++中的数据变量以及类对象,但它不可能通过MASM指令和操作符来定义数据及对象。尤其你还不能使用DB, DW, DD, DQ, DT和DF等定义指令以及DUP和This操作符。汇编中的结构记录也不是可用的。内联汇编也不支持directives STRUC, RECORD, WIDTH, 和 MASK指令。不过,在内联汇编可以使用到一个_emit宏指令,它类似于MASM中的DB指令,它可以在本区域内定义出一个字节型,虽然它每次只能定义一个字节出来,但还是可以用它来定义出一个字符串,请看示例:

    #define randasm __asm _emit 0x4A __asm _emit 0x43 __asm _emit 0x4B

    __asm
    {
        randasm
    }

    虽然内联汇编不支持MASM中的很多指令,但它支持EVEN 和 ALIGN指令。它们被用于那些需要使用align labels来指定分界线的汇编指令。
    内联汇编不可以是一个宏汇编程序,你不可以使用MASM中的宏定义指令以及宏操作符。但内联汇编是可以使用C/C++中的预理指令来定义宏。

    在处理段时,你只能使用寄存器,而不是通过名字,因为在内联汇编中这是非法的。而且段必须显式地使用寄存器,如: ES:[BX]

    在内联汇编使用操作符LENGTH, SIZE, 和 TYPE可以来对变量以及类型进行长度的测量,你可以使用它们来求得C/C++中的变量及类型的长度:

    *LENGTH操作符可以返回在一个变量数组中的元素个数,如果返回为1则代表这不是一个变量数组。

    *SIZE操作符可以求得一个变量及类型的总长度。这个值也可以由LENGTH与TYPE积来求得。

    *TYPE操作符可以求得一个变量及类型的长度,与SIZE不同的是,如果变量名是一个数组的话,则返加这个数组中单个元素的长度。

    具体情况请看下表:

    __asm C Size
    LENGTH arr sizeof(arr)/sizeof(arr[0]) 8
    SIZE arr sizeof(arr) 16
    TYPE arr sizeof(arr[0]) 2

    包含着内联汇编的程序可以使用/Zi选项编译,从而来进行代码级的调试工作。这样,你就可以同时在C/C++程序段与内联汇编段中设置断点,如果你使用/Fas选项允许混合汇编与C/C++源程序调试方式,那么你就可以看到混合着汇编与源程序的代码集合。

      Visual C++编译器允许你在内联汇编程序中使用Intel处理器的MMX指令集。不过如果使用MMX指令集编译器会发生警告。更多的信息请查看MSDN的Web站点。


    二、 关于内联汇编的具体使用说明

      因为内联汇编不需要编译与链接过程,因此它比一个汇编程序更为方便。由于它能够访问C/C++中的变量及函数,所以它能够更好和你C/C++代码融为一体。内联汇编可以在以下方面进行编程:

    *用汇编语言编写函数。
    *使用汇编语言来产生速度最优化代码段。
    *直接使用汇编语言来对硬件进行访问。
    *为naked函数调用编写保护现场和恢复现场代码( prolog and epilog code)

    如果你计划在不同机器上运行程序的话,那么你应该分别放置不同机种的专一汇编代码。因为内联汇编有一定的机器专一性,它不完全支持所有MASM中的宏与数据类型。

    VC不支持C++中的asm关键字,所以你需要使用__asm(两个下划线)关键字来编写你的内联汇编代码。

    你可以使用这个关键字来编写一个内联代码段,如:

    __asm
    {
        mov al, 2
        mov dx, 0xD007
        out al, dx
    }

    也可以只编写一行式的内联代码,如:

    __asm mov al, 2
    __asm mov dx, 0xD007
    __asm out al, dx

    以上两段代码是同义的。但是第一种写法比较有好处(advantages?),它可以与C源码明显的区别开来,而且避免重复输入不必要__asm关键字。 在内联汇编代码段中以下的C语言元素是可以被应用的:

    *符号,包括跳转标签,变量名和函数名。(所使用C/C++符号必须在其使用名域之内。)
    *C/C++常量,包括const符号化常量和共用体(enum)中的常量
    *宏以及预处理表达式。
    *C/C++风格的注释,//,/*,*/
    *类型名
    *typedef定义的类型名

    三、在内联汇编代码中使用C操作符

      在内联汇编中不能使用C/C++专有的操作符,诸如:<<,虽然,有一些操作符是MASM与C中都在使用的,比如:*操作符。但在内联汇编中被优先解释为汇编操作符。例如,在C中方括号是用来访问数组的元素的。C将它解释为首地址+单个元素长度*元素序号。而在内联汇编中,则将它解释为首地址+方括号中定义的数量。是对一个地址的字节偏移量。这一点是在编程中应该特注意的。所以以下这一段代码是错误的

    int array[10];
    __asm mov array[6], 0 ; 期望达到C中的array[6] = 0功能,但这是错误的。

    正确的代码如下:

    __asm mov array[6 * TYPE int], 0 ;
    array[6] = 0;

    在内联汇编中使用C/C++符号(如前面如述,符号包括常量名,变量名,函数名以及跳转标签)应注意以下几点:

    *所使用C/C++符号必须在其使用名域之内。
    *一般的情况下,一句汇编语句只允许出现一个C/C++符号。在LENGTH, TYPE, 和 SIZE表达式中则可以使用多个C/C++符号。
    *就像C语言一样,在内联汇编中调用函数之前,必须显式的声明函数。否则编译器将会报错。
    *注意在内联汇编中不能使用那些与MASM中保留字相同的C/C++符号。
    *注意C/C++中的类,结构体以及共用体在内联汇编中不直接使用。
    下面将举几个关于使用C/C++符号的例子。
    如果先前C已经定义了一个变量var,那么则内联汇编可以访问这个变量如下:

    __asm mov eax, var ;将变量var中的值赋给eax寄存器中。

    如果有一个结构体first_type和一个实例hal:

    struct first_type
    {
        char *weasel;
        int same_name;
    } hal;

    在访问hal对象时,则必须如下:

    __asm
    {
        mov ebx, OFFSET hal ;取得hal对象的首地址
        mov ecx, [ebx]hal.same_name ;加上same_name偏移值,则可以访问到成员same_name
        mov esi, [ebx]hal.weasel ;加上weasel偏移值。
    }

    下面是一个内联汇编如何实现一个函数的例子:

    #include <stdio.h>
    int power2( int num, int power );

    void main( void )
    {
        printf( "3 times 2 to the power of 5 is %d/n", /
        power2( 3, 5) );
    }

    int power2( int num, int power )
    {
        __asm
        {
            mov eax, num ; 取得第一个参数
            mov ecx, power ; 取得第二个参数
            shl eax, cl ; EAX = EAX * CL
        }
        //在函数中,返回值是由eax负责往回传递的。(顺便问一句ax与eax有什么不同啊?是不是一样的?)
    }

    因为内联函数中没有return,所以在上面的例子中,编译器会报出警告。还好,不像Java一样,少一个多一个return都会编译不通过。你可以使用宏#pragma warning来关掉警告器。在pascall式函数中堆栈的复位是由函数负责的,而不是调用者。在上面的例子中,由是在C函数中内部嵌入汇编来完成汇编函数的。在C函数出口处,C编译器会自动添加复栈指令,而不必自己添写。那反而会使系统混乱. 在内联汇编中跳转指令(包括条件跳转),可以跳转到C语言goto能到的所有地方。Goto也可以跳到内联汇编中定义的标签,示例如下:

    void func( void )
    {
        goto C_Dest; /* Legal: correct case */
        goto c_dest; /* Error: incorrect case在C中大小写区分。*/

        goto A_Dest; /* Legal: correct case */
        goto a_dest; /* Legal: incorrect case */

        __asm
        {
            jmp C_Dest ; Legal: correct case
            jmp c_dest ; Legal: incorrect case

            jmp A_Dest ; Legal: correct case
            jmp a_dest ; Legal: incorrect case

            a_dest: ; __asm label
        }

        C_Dest: /* C label */
        return;
    }

    另外,在给标签起名时尽量避免与C内部的或已经使用了的标签名重名,如果那样的将会出现灾难性的程序错误。因此,在起名时最好追查一下是否这个名字已经被使用了。在引用函数时,应注意参数的从右向左方向地压栈。比如有一个函数是 int CAdd (int a,int b) 则应该如此调用:

    __asm
    {
        mov eax,2;
        push; 参数b等于2
        mov eax,3;
        push; 参数a等于3
        call CAdd;调用CAdd函数
        mov Result,eax;所有函数的返回值都被存放在eax。于是,Result等于5
    }

    注意内联汇编无法调用重载函数,因为被重载函数名与原函数名不一样。所以如果你需求调用的话, (我记得vckbase中有关于重载函数的文章),就不要定义重载函数,且C++函数必须使用extern "C"关键字来定义。因为C中的预处理指令#define是字符代换,所以你可以使用#define来定义一个汇编宏,例如:

    #define PORTIO __asm /
    /* Port output */ /
    { /
        __asm mov al, 2 /
        __asm mov dx, 0xD007 /
        __asm out al, dx /
    }

    以上,就是内联汇编的基本使用描述。由于,本人的英文并不是太好,所以写出来的文章有些不连续,而且大部分话是我自己说的,或许还会译错的地方,还请大家指教见谅。以下是我自己写的一段关于类,结构体的示例:

    #include <iostream.h>
    struct MyData
    {
        int nMember1;
        int * lpMember2;
    };

    void main()
    {
        MyData sample;

        __asm//这是对成员变量赋值
        {
            mov eax,12;
            mov sample.nMember1,eax;
        }
        cout <<sample.nMember1<<endl;

        __asm//这是对成员指针赋值
        {
            lea eax,sample.nMember1;
            mov sample.lpMember2,eax;
        }
        cout <<*sample.lpMember2<<endl;

        __asm//这是对指针所指向的变量赋值
        {
            mov ebx,sample.lpMember2;
            mov eax,5;
            mov [ebx],eax;
        }
        cout <<sample.nMember1<<endl;
    }

    不过,对于成员函数的调用仍没有成功。请各位高手帮忙解决这个问题。谢谢。 

    展开全文
  • ARM嵌入式开发中的GCC内联汇编简介 在针对ARM体系结构的编程中,一般很难直接使用C语言产生操作协处理器的相关代码,因此使用汇编语言来实现就成为了唯一的选择。但如果完全通过汇编代码实现,又会过于复杂...
  • 在Visual C++中使用内联汇编 目录:  内联汇编的优缺点  内联汇编关键字  在__asm块中使用汇编语言  在__asm块中使用C/C++语言元素  寄存器使用  转跳  调用函数  一个例子  后话 ...
  • C语言预处理与宏定义1.C语言预处理理论1.1 ...内联函数和inline关键字 1.C语言预处理理论 1.1 由源码到可执行程序的过程 当写了一个test.c文件,对它进行编译似乎是直接生成了可执行文件a.out: peco@ubuntuz:/mnt/hgfs
  • 习题(c++期末知识复习2--)

    千次阅读 2020-06-20 16:17:38
    下列关于内联函数的说法中不正确的是(D ) A. 内联函数体内不能有循环语句 B. 内联函数体内不能有switch语句 C. 内联函数必须先定义后调用 D. 内联函数在被调用时发生控制转移 关于递归函数的说法中,不正确的是(C...
  • 【C++】内联函数分析

    千次阅读 2018-09-02 18:25:10
    一、为什么要使用内联函数:  当程序执行函数调用指令时,CPU存储函数调用之后的指令的存储器地址,复制函数的参数在堆栈上,最后将控制转移到指定的函数。然后,CPU执行功能代码,将功能返回值存储在预定义的...
  • 当然,了解这些,除了为编译器设计做铺垫,对于学习C语言嵌入GCC内联汇编以及读懂linux核心代码都会有帮助,也会加深对C语言底层的理解。 实验分析 最小框架 我是通过 gcc -S 这个命令入的坑。我相信写过C语言...
  • 下列关于这5个系统数据库的说法错误的是______。 A.master数据库记录了所有其他数据库的数据库文件的物理存储位置 B.SQL Server实例的作业信息存储在msdb数据库中 C.如果tempdb损坏,则需要使用备份进行恢复 D....
  • 第2章 汇编及逆向工程基础 2.1 导言 本章,我们将介绍一些基础性的内容,包括汇编语言、Intel架构的处理器,以及进一步学习时需要掌握的概念。本书着眼于32位Intel架构(IA-32)的汇编语言,涉及Windows及...
  • Java面试题整理(汇编)

    2020-04-24 17:30:39
    2.下列说法中,错误的有()。3.Java程序中程序运行入口方法main的签名正确的有()。4.众所周知,在Java语言中,main()方法是程序的入口方法,在程序运行时,最先加载的就是main()方法,但这是否意味着main()方法就是程序运行...
  • 小米软件开发笔试题

    千次阅读 2020-10-10 16:21:53
    1.下列关于设计模式说法错误的是( ) 正确答案: B A。装饰器模式在实现过程中一般不会更改被封装对象的接口定义 B。适配器模式以不改变被适配对象的接口定义为目的对其进行改造(错误) C。用饿汉方式实现的单列模式...
  • 但是注意,在内联汇编仍可使用.  suicide()已弃用, 请使用 selfdestruct().  sha3()已弃用,请使用 keccak256(). 构造函数  构造函数必须用 constructor关键字定义. 之前,并未强制要求,既可以用合约...
  • inline使用方法 转

    2020-05-11 11:08:14
    大学在教科书上学习过inline函数,定义为inline函数之后,会省去函数调用的开销,直接嵌套汇编代码,取代函数调用,提高效率。工作后项目中也很少用到inline来定义函数,近几天在研读google的google c++ style guide...
  • 实参和形参在数量上,类型上,顺序上应严格一致, 否则会发生“类型不匹配”的错误。 函数调用中发生的数据传送是单向的。 即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。 因此在函数调用过程中...
  • 当然,我的理解并不一定100%正确,难免会有一些错误或是没有根据的猜想,如果读者们觉得有问题,我很欢迎大家指正我。 目前我仍然在边读书边理解,还未完全吃透代码,甚至连1%的代码可能都没见过
  • 于是在五一期间我按照留言的说法,修改了一下程序,并进行了一波基于搜索引擎的研究。 嘿,你猜怎么着? 我还真的研究出了一点有意思的东西。 先说结论:final 关键字影响了程序的结果。 在上面这个案例中,final ...
  • 还有一种说法是d8最初叫developer shell,因为d后面有8个字符,因此简写为d8,类似于i18n(internationalization)这样的简写。 参考:Using d8 安装 D8 方法一:自行下载编译 v8 google 下载及编译使用 官方文档:...
  • CIW认证题库-页面设计与制作

    万次阅读 2012-01-06 20:55:45
    2、对于“行为”概念的说法正确的是:( ) D行为代码是客户端JavaScript代码,不在服务器中而是运行在浏览器中 3、在对图像被“重新取样”之后,图像会发生什么样的变化 (C) C该文件在浏览器上的显示尺寸发生...
  • alert(obj.id) //正确的名字 obj = objText.getAttribute("obj"); alert(obj.id) //null 在IE下没有问题, FF对setAttribute来说,第2个参数都是字符串型的!!! 所以如果第2个参数是对象时,相当于调用对象的 ...
  • 什么是JavaScript

    2020-07-07 21:38:18
    目录1、广义的定义2、...9.1、内部 JavaScript9.2、外部 JavaScript9.3、内联 JavaScript 处理器10、脚本调用策略11、参考 1、广义的定义 JavaScript 是一种脚本,一门编程语言,它可以在网页上实现复杂的功能,
  • 此方法优于强制PC或SP正确对齐,因为PC或SP未对齐通常表示软件错误,例如软件中的地址损坏。 有多种类型的对齐检查: 每当试图执行AArch64中未对齐PC获取的指令时,程序计数器对齐检查会生成与指令获取相关的异常。...
  • 成员函数指针与高效C++委托 (delegate)

    千次阅读 2015-10-20 21:09:17
    之后我们会看到编译器该如何来实现高效的委托, 最终, 利用上面关于成员函数指针的知识, 我会实现一个在大多数编译器上都高效的委托. 比如, 在 Visual C++(6.0, .NET 和 .NET 2003) 调用一个单目标的委托只会产生两...
  • 面试笔试知识点积累

    2015-05-06 14:37:28
    20:对于#include <filename.h> 和 #include “filename.h”,以下说法错误的是( )。 A#include 只搜索标准库路径 B#include “filename.h”只搜索用户工作路径 C#include 搜索范围比#include “filename.h...
  • 全面解析《嵌入式程序员应该知道的16个问题》

    千次阅读 多人点赞 2015-07-24 23:58:21
    人们经常声称这里有几个问题是那种要翻一下书才能回答的问题,我同意这种说法。当我写这篇文章时,为了确定语法的正确性,我的确查了一下书。但是当我被面试的时候,我期望被问到这个问题(或者相近的问题)。因为在...
  • 深入理解Java 8 Lambda

    千次阅读 2018-09-11 14:11:43
    关于 深入理解 Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法) 深入理解 Java 8 Lambda(类库篇——Streams API,Collector 和并行) 深入理解 Java 8 Lambda(原理篇——Java 编译器如何处理...
  • 通过宏定义和内联函数(inline),让编译器提前完成部分工作。无需在程序运行时分配内存,能够实现类似函数的功能,却没有函数调用的压栈、弹栈开销,效率会比较高。 ... ... 3. AMS平台技术选型的背景 就...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 758
精华内容 303
关键字:

关于内联汇编的一致说法错误的是

友情链接: CompanyCMS_V12_HTML_ZY.zip