新书推荐: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)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/846145.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

介绍谷歌开发工具中的 Application 面板

在现代 Web 开发中&#xff0c;浏览器开发工具&#xff08;DevTools&#xff09;是开发者的利器。谷歌 Chrome 的开发者工具提供了丰富的功能&#xff0c;帮助开发者调试、优化和分析他们的 Web 应用程序。本文将深入介绍 DevTools 中的 “Application” 面板&#xff0c;帮助你…

Spark SQL数据源 - Parquet文件

当使用Spark SQL处理Parquet文件时&#xff0c;你可以使用spark.read.parquet()方法从文件系统中加载Parquet数据到一个DataFrame中。Parquet是一种列式存储格式&#xff0c;非常适合用于大数据集&#xff0c;因为它提供了高效的压缩和编码方案。 以下是一个简单的例子&#x…

prometheus的rules配置

说明&#xff1a;本文介绍prometheus中的规则配置&#xff1b; 规则说明 groups:- name: MySQLAlertsrules:- alert: MysqlDownexpr: mysql_up 0for: 0mlabels:severity: criticalannotations:summary: MySQL down (实例&#xff1a;{{ $labels.instance }})description: &q…

VMware Workstation中WinXP联网问题

我一直以为我的虚拟机上的XP没有联网 因为 蒙了半天&#xff0c;发现是因为这个网址打不开&#xff0c;不是没有网 太傻了 不如在cmd命令行中通过ping baidu.com来判断是否联网

JavaScript中对象的增删改查

1. 增&#xff08;添加属性&#xff09; let obj {}; // 添加一个属性 obj.name John Doe; // 或者使用方括号语法添加属性&#xff08;这对于动态属性名很有用&#xff09; let propName age; obj[propName] 30; console.log(obj); // 输出: { name: John Doe, …

【康耐视国产案例】Nvidia/算能+智能AI相机:用AI驱动 | 降低电动车成本的未来之路

受环保观念影响、政府激励措施推动与新能源技术的发展&#xff0c;消费者对电动汽车(EV)的需求正在不断增长&#xff0c;电动汽车已经成为了未来出行方式的重要组成部分。然而&#xff0c;电动汽车大规模取代燃油汽车的道路还很漫长。最大的障碍就是电动汽车的售价相对过高。尽…

【Python面试50题】

1. **基础概念** 1. Python 是解释型还是编译型语言&#xff1f; 2. 什么是 Python 的 GIL&#xff08;全局解释器锁&#xff09;&#xff1f; 3. 如何理解 Python 中的可变与不可变数据类型&#xff1f; 4. 解释一下 Python 中的 pass 语句。 5. Python 中的列…

基于昇腾910B训练万亿参数的语言模型简介

基于昇腾910B训练万亿参数的语言模型 Abstract 在本工作中&#xff0c;作者开发了一个系统&#xff0c;该系统在Ascend 910 AI处理器集群和MindSpore框架上训练了一个万亿参数的语言模型&#xff0c;并提出了一个含有1.085T参数的语言模型&#xff0c;名为PanGu-。 从PanGu-[…

3.基础光照

从宏观上讲渲染包含了两大部分&#xff1a;决定一个像素的可见性&#xff0c;决定一个像素的光照计算 而光照模型就是用于决定在一个像素上决定怎样的渲染光照计算。 一、我们是如何看到这个世界的 1.光源 实时渲染中&#xff0c;我们通常把光源当成一个没有体积的点&#…

远程服务器上,再次配笔记1、2、11、12相同的深度学习和gcc环境

文章目录 参考文献 创建环境在(zgp_m3dm)中安装会显示zgp_m3dm_main环境中的情况满足(base) ~/zgp/M3DM-repetition conda deactivate 再次安装如果服务器本身不能联网&#xff0c;需下载pysocks包以设置反向代理&#xff08;具体可参考之前的笔记10&#xff09;&#xff1a; 再…

简易版本的QFD质量屋

比如餐馆要考虑什么因素最重要&#xff0c;这里列出好吃&#xff0c;快速&#xff0c;便宜三类问题&#xff0c;然后设置上图的权重&#xff0c; 然后设置9&#xff0c;3&#xff0c;1三类因子&#xff0c;9比如是最重要的&#xff0c;3&#xff0c;1&#xff0c;依次没那么重要…

[vue2项目]vue2+supermap[mapboxgl]+天地图之地图的初始化

Supermap参考教程 天地图 一、安装 1、终端:npm install supermap/vue-iclient-mapboxgl 2、在package.json文件的dependencies查看supermap/vue-iclient-mapboxgl依赖是否安装成功。 3、在mian.js全局引入 import VueiClient from supermap/vue-iclient-mapboxgl; Vue.use(…

stm32和nrf24L01病房呼叫系统的实现

stm32和nrf24L01病房呼叫系统的实现 文章目录 stm32和nrf24L01病房呼叫系统的实现第一章 项目背景与意义第一节 病房呼叫系统的现状1.技术现状2.存在的问题 第二节 STM32F1、nRF24L01、OLED技术介绍1.STM32F1技术介绍2.nRF24L01技术介绍3.OLED技术介绍 第三节 项目目标与预期成…

牛客网刷题 | BC106 K形图案

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 KiKi学习了循环&am…

Java 基础面试300题 (201-230)

Java 基础面试300题 &#xff08;201-230&#xff09; 201.下面代码片段的输出是什么&#xff1f; Predicate<Integer> numberChecker (num)–> num > 20; int input 10; System.out.println(input” greater than 20–”numberChecker.test(input)); //Line 1…

perl: URI::rtsp 是用来处理RTSP协议的的URI的模块。

在Perl中&#xff0c;URI::rtsp 是用来处理RTSP&#xff08;Real Time Streaming Protocol&#xff0c;实时流协议&#xff09;的URI的模块。 以下是一些使用 URI::rtsp 的方法&#xff1a; 1.创建一个RTSP URI use URI::rtsp;my $uri URI::rtsp->new("rtsp://user…

WPS部分快捷操作汇总

记录一些个人常用的WPS快捷操作 一、去除文档中所有的超链接&#xff1a; 1、用WPS打开文档&#xff1b; 2、用Ctrla全选&#xff0c;或者点击上方的【选择】-【全选】&#xff0c;选中文档全部内容&#xff1b; 3、按CTRLSHIFTF9组合键&#xff0c;即可一次性将取文档中所有…

AI学习指南机器学习篇-线性回归统计推断

AI学习指南机器学习篇-线性回归统计推断 在机器学习领域&#xff0c;线性回归模型是最为常见且基础的模型之一。而统计推断则是为了更好地理解数据背后的规律而产生的一门重要学科。本文将探讨线性回归模型与统计推断的关系&#xff0c;重点讨论参数估计的置信区间和假设检验。…

Linux的程序管理2:设置优先级,程序的查看与处理---ps命令详解与kill,killall,top,renice,nice搭配使用管理linux

前言 程序的管理除了前文说到的前后台执行&#xff0c;离线执行。还包括下面几个方面&#xff1a; 1&#xff1a;如何找到最耗费系统资源的程序&#xff1f; &#xff08;使用top命令找到PID然后排查原因&#xff09; 2&#xff1a;设置程序的优先级&#xff0c;让其被执行的概…

Leetcode 第 397 场周赛题解

Leetcode 第 397 场周赛题解 Leetcode 第 397 场周赛题解题目1&#xff1a;3146. 两个字符串的排列差思路代码复杂度分析 题目2&#xff1a;思路代码复杂度分析 题目3&#xff1a;3148. 矩阵中的最大得分思路代码复杂度分析 题目4&#xff1a;3149. 找出分数最低的排列思路代码…