新书推荐:11.6 调用约定

2024-06-02 10:36
文章标签 推荐 调用 约定 11.6 新书

本文主要是介绍新书推荐:11.6 调用约定,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本节必须掌握的知识点:

        函数调用约定

11.6.1 函数调用约定

什么是函数调用约定?就是告诉编译器:怎么传递参数,怎么传递返回值,怎么平衡堆栈,函数怎样命名。当一个程序中所有的函数都是自己实现的,则不需要和任何人约定,但是如果程序中调用第三方的函数,则需要和对方约定上述事宜了。

常见的调用约定有__stdcall,__cdecl,__fastcall, __pascal,Windows32位系统中,微软就开始使用__fastcall调用约定了,到了x64系统,则同一采用__fastcall调用约定了(参见《X86汇编基础教程》 第三部分64位汇编)。

实验一百二十三:__cdecl调用约定

在VS中新建项目11-6-1.c:

/*

   __cdecl调用约定

*/

#include <stdio.h>

#include <stdlib.h>

int funAdd(int x, int y)

{

    return x + y;

}

int main(void)

{

    int rel = funAdd(1, 2);//这里是堆栈传参

    /*

        00401068   push        2  //将2压入堆栈

        0040106A   push        1  //将1压入堆栈

        0040106C   call        @ILT+0(funAdd)(00401005)//调用者

        00401071   add         esp, 8  //平衡堆栈(外平栈)

        00401074   mov         dword ptr[ebp - 4], eax

    */

    printf("%d\n",rel);

    system("pause");

    return 0;

}

●运行结果:

3

请按任意键继续. . .

●参数传递:有堆栈传参、寄存器传参、堆栈+寄存器传参、call后续区传参这四种方式。

那么怎么决定参数传递顺序的呢?要看具体的调用约定。

●调用约定:

下面列举了常见的调用约定有三种形式:

上面的代码中,并没有用告诉编译器是哪种调用约定,在VS下默认的调用约定是__cdecl,我们来一一验证下。

参数压栈顺序:从右往左入栈,funAdd(1,2);执行从右往左压栈顺序,先push 2再 push 1,与__cdecl执行参数压栈顺序是一致的。我们接着看平衡堆栈,__cdecl的平衡堆栈要求是:调用者清理栈,且是外平栈。我们再来看下我们写的代码, call  @ILT+0(funAdd) (00401005),  funAdd(0x00401005)是当前的函数,main函数调用了 funAdd(0x00401005)函数,所以main函数是调用者,接着add  esp,8,是外平栈。

以上完全符合__cdecl调用约定规则。

我们可以函数定义中指定函数的调用约定,例如:

int __cdecl  funAdd(int x,int y) //这样键入__cdecl就可以了

实验一百二十四:__stdcall调用约定

在VS中新建项目11-6-2.c:

/*

   __stdcall调用约定

*/

#include <stdio.h>

#include <stdlib.h>

int __stdcall  funAdd(int x, int y)

{

    return x + y;

}

int  main(void)

{

    int rel = funAdd(1, 2);

    printf("%d\n", rel);

    system("pause");

    return 0;

}

●运行结果:

3

请按任意键继续. . .

●代码分析:

我们来看以下反汇编代码:

int __stdcall  funAdd(int x, int y)

{

00301847  dec         dword ptr [ebx+45030845h] 

    return x + y;

0030184D  or          al,5Fh 

}

0030184F  pop         esi 

00301850  pop         ebx 

00301851  add         esp,0C0h 

00301857  cmp         ebp,esp 

00301859  call        __RTC_CheckEsp (0301226h) 

0030185E  mov         esp,ebp 

00301860  pop         ebp 

00301861  ret         8   ;内平栈

int  main(void)

{

00301880  push        ebp 

00301881  mov         ebp,esp 

    int rel = funAdd(1, 2);

003018A8  push        2  ;从右往左入栈

003018AA  push        1 

003018AC  call        _funAdd@8 (03011E5h) 

003018B1  mov         dword ptr [rel],eax 

    printf("%d\n", rel);

003018B4  mov         eax,dword ptr [rel] 

003018B7  push        eax 

003018B8  push        offset string "%d\n" (0307B30h) 

003018BD  call        _printf (030104Bh) 

003018C2  add         esp,8 

    system("pause");

    return 0;

003018DC  xor         eax,eax 

}

__stdcall调用约定:参数从右往左压入堆栈,且是函数自己本身清理堆栈。

实验一百二十五:__fastcall调用约定

在VS中新建项目11-6-3.c:

/*

   __fastcall调用约定

*/

#include <stdio.h>

#include <stdlib.h>

int __fastcall funAdd(int x, int y, int i, int j)

{

    return x + y + i + j;

}

int  main(void)

{

    int rel = funAdd(1,2,3,4);

    printf("%d\n", rel);

    system("pause");

    return 0;

}

●运行结果:

10

请按任意键继续. . .

●代码分析:

我们来看以下反汇编代码:

int __fastcall funAdd(int x, int y, int i, int j)

{

00B51720  push        ebp 

00B51721  mov         ebp,esp 

 

    return x + y + i + j;

00B51750  mov         eax,dword ptr [x] 

00B51753  add         eax,dword ptr [y] 

00B51756  add         eax,dword ptr [i] 

00B51759  add         eax,dword ptr [j] 

}

00B5176C  mov         esp,ebp 

00B5176E  pop         ebp 

00B5176F  ret         8  ;平衡2个堆栈参数

int  main(void)

{

00B51890  push        ebp 

00B51891  mov         ebp,esp 

 

    int rel = funAdd(1,2,3,4);

00B518B8  push        4  ;第四个参数

00B518BA  push        3  ;第三个参数

00B518BC  mov         edx,2   ;第二个参数

00B518C1  mov         ecx,1   ;第一个参数

00B518C6  call        @funAdd@16 (0B51082h) 

00B518CB  mov         dword ptr [rel],eax 

    printf("%d\n", rel);

00B518CE  mov         eax,dword ptr [rel] 

00B518D1  push        eax 

00B518D2  push        offset string "%d\n" (0B57B30h) 

00B518D7  call        _printf (0B5104Bh) 

00B518DC  add         esp,8 

    system("pause");

    return 0;

00B518F6  xor         eax,eax 

}

由上述反汇编代码可知,第1,2个参数使用ecx和edx寄存器传参,第3,4个参数使用堆栈传参,堆栈传参仍然是从右往左顺序入栈。堆栈平衡为内平衡,被调函数负责平衡2个参数占用的8个字节堆栈空间。

由于寄存器传参的速度最快,因此称为fastcall调用约定,可惜32位程序只能使用ecx和edx两个寄存器参与传参,剩余参数使用堆栈传参。x64处理器中的fastcall调用约定可以使用rcx,rdx,r8,r94个寄存器传参,剩余参数使用堆栈传参。

 

总结

__fastcall通过寄存器传参,传递参数不破坏堆栈的平衡,所以它不需要平栈,它的执行效率最高。但是当它的参数的数量大于2时,它就会采取堆栈+寄存器的方式传参,反而会降低执行效率。

C语言中默认采用的调用约定是“_cdecl”;_stdcall为WINAPI的调用约定,_fastcall为Windows内核中常见的调用约定。此外还有C++中有this call调用约定(_thiscall)。

这篇关于新书推荐:11.6 调用约定的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1023722

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

防近视护眼台灯什么牌子好?五款防近视效果好的护眼台灯推荐

在家里,灯具是属于离不开的家具,每个大大小小的地方都需要的照亮,所以一盏好灯是必不可少的,每个发挥着作用。而护眼台灯就起了一个保护眼睛,预防近视的作用。可以保护我们在学习,阅读的时候提供一个合适的光线环境,保护我们的眼睛。防近视护眼台灯什么牌子好?那我们怎么选择一个优秀的护眼台灯也是很重要,才能起到最大的护眼效果。下面五款防近视效果好的护眼台灯推荐: 一:六个推荐防近视效果好的护眼台灯的

智能交通(二)——Spinger特刊推荐

特刊征稿 01  期刊名称: Autonomous Intelligent Systems  特刊名称: Understanding the Policy Shift  with the Digital Twins in Smart  Transportation and Mobility 截止时间: 开放提交:2024年1月20日 提交截止日

【LabVIEW学习篇 - 21】:DLL与API的调用

文章目录 DLL与API调用DLLAPIDLL的调用 DLL与API调用 LabVIEW虽然已经足够强大,但不同的语言在不同领域都有着自己的优势,为了强强联合,LabVIEW提供了强大的外部程序接口能力,包括DLL、CIN(C语言接口)、ActiveX、.NET、MATLAB等等。通过DLL可以使用户很方便地调用C、C++、C#、VB等编程语言写的程序以及windows自带的大

string字符会调用new分配堆内存吗

gcc的string默认大小是32个字节,字符串小于等于15直接保存在栈上,超过之后才会使用new分配。

开放式耳机好用?平价开放式耳机推荐?四款开放式的蓝牙耳机推荐

开放式耳机好用吗?有平价些的开放式耳机推荐吗?那这两个问题的回答当然是肯定的。 首先开放式耳机好不好用取决于对耳机的需求,因为开放式耳机其实是比较适用于需要注意周围环境、需要‌长时间佩戴舒适以及需要频繁与人交流的场景中,在这些场景下使用开放式耳机的话就会比较有优势。就例如跑步骑行健身等运动的时候,能够兼得佩戴舒适度的同时,增加一定的安全性;还有在办公学习的时候,会很适合长时间佩戴,能够方便和

京东物流查询|开发者调用API接口实现

快递聚合查询的优势 1、高效整合多种快递信息。2、实时动态更新。3、自动化管理流程。 聚合国内外1500家快递公司的物流信息查询服务,使用API接口查询京东物流的便捷步骤,首先选择专业的数据平台的快递API接口:物流快递查询API接口-单号查询API - 探数数据 以下示例是参考的示例代码: import requestsurl = "http://api.tanshuapi.com/a

vue 父组件调用子组件的方法报错,“TypeError: Cannot read property ‘subDialogRef‘ of undefined“

vue 父组件调用子组件的方法报错,“TypeError: Cannot read property ‘subDialogRef’ of undefined” 最近用vue做的一个界面,引入了一个子组件,在父组件中调用子组件的方法时,报错提示: [Vue warn]: Error in v-on handler: “TypeError: Cannot read property ‘methods