我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C语言函数调用约定和返回值详情

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C语言函数调用约定和返回值详情

 一、函数调用约定

  • _cdecl:C调用约定
  • _stdcall:Windows标准的调用约定
  • _fastcall:快速调用约定
  • _thiscall:C++的成员函数调用约定

以上的函数调用约定入参都是从右向左,只有PASCAL从左向右

函数调用约定不同,会影响函数生成的符号名,函数入参顺序,形参内存的清理者

1. 影响函数生成的符号名

在一个文件中写_cdecl的函数声明:

在另一个文件中写_stdcall的函数定义:

我们编译链接一下:

链接器找不到__cdecl sum这个函数调用的定义,将声明的地方改成__stdcall就可以链接成功

2. 影响形参内存的释放者

_stdcall

形参内存还是由调用方开辟

ret表示把栈顶元素的值(调用处下一条指令的地址)赋给PC寄存器,并出栈栈顶元素(修改esp)
ret 8表示在ret操作的基础上,执行执行指令add esp, 8

_fastcall

可以看到在_fastcall调用约定中,call指令前面并没有push操作,而是通过寄存器把实参传递到形参(没有压栈出栈,速度很快),实参在调用方栈帧上,形参在被调用方栈帧上

在_fastcall调用约定中,最多只能通过寄存器将最左边8字节的实参带给形参,多于8字节的实参还是通过push的方式带给调用方的形参

我们给sum传入三个参数,看看是谁释放形参内存 :

这就很清楚了,一共3个参数,左边的2个参数通过寄存器传递不需要清理内存,只有一个形参内存需要释放,所以显示ret 4

对于sum函数的第一个局部变量temp,在_cdecl和_stdcall中都是通过ebp-4访问的,形参是通过ebp正向偏移访问,因为形参内存在调用方的栈帧上

而在_fastcall中是通过寄存器把左边的8字节实参带给sum的形参,并存放在sum函数的栈帧上:

mov edx, dword ptr [ebp-8]
mov ecx, dword ptr [ebp-4]

所以对于sum函数的第一个局部变量temp,只能通过ebp-0Ch访问

_thiscall

对参数个数不确定的,调用者清理堆栈,否则被调用者清理堆栈

二、函数的返回值

函数的返回值分为内置类型(char、short、int、long、float、double等)、结构体类型、union、enum等

1. 0 < 返回值 <= 4字节

通过eax寄存器带出

2. 4字节 < 返回值 <= 8字节

#include <stdio.h>

typedef struct  {
	int a;
	int b;
}Data;

Data sum(Data a, Data b) {
	Data temp = { 0 };
	temp.a = a.a + b.a;
	return temp;
}
int main() {
	Data a = { 10 }; 
	Data b = {20};  
	Data ret = { 0 }; 
	ret = sum(a, b);
	return 0;
}

可以看到,4字节 < 返回值 <= 8字节时,通过eax和edx寄存器带出

3. 返回值 > 8字节

#include <stdio.h>

typedef struct  {
	int a[20];
}Data;

Data sum(Data a, Data b) {
	Data temp = { 0 };
	temp.a[0] = a.a[0] + b.a[0];
	return temp;
}

int main() {
	Data a = { 10 };
	Data b = {20};
	Data ret = { 0 };
	ret = sum(a, b);
	return 0;
}

压参数的时候,没有使用push指令,因为寄存器不够用,故使用了循环拷贝的方法,从实参的空间拷贝到形参的空间

产生临时量有三个地方:函数调用前,函数调用时return的地方,函数调用完成时。在接收大于8字节返回值时,是在函数调用前产生临时量,并把临时量内存的地址压栈,而这个临时量是用来接收返回值的

我们看到不仅仅压栈了实参a、b,还压栈了临时量的地址,可以把sum函数简单理解为如下形式:

Data sum(void* tmp_address, Data a, Data b);

我们看一下sum函数中return时的汇编指令是如何待会80字节的返回值的

最后通过eax把临时量的地址带出来,调用函数就可以通过eax拿到sum函数的返回值了

如果临时量在函数调用前产生,那被调用函数返回的时候,肯定是通过ebp+8访问临时量并写入返回值。因为ebp指向的空间保存了调用函数的栈底地址,ebp+4指向的空间保存了call指令下一条指令的地址,ebp+8指向最后一个压栈的实参,即用于带出返回值的临时量的地址

返回方式:

  • 返回值空间在[1,4],用eax寄存器
  • 返回值空间在[5,8],用eax、edx寄存器
  • 返回值空间大于8字节时,函数调用前产生临时量用于存储返回值,并把这个临时量的地址作为最后一个实参压栈,在被调用函数中通过ebp+8访问该临时量的地址

到此这篇关于C语言函数调用约定和返回值详情的文章就介绍到这了,更多相关C函数调用内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C语言函数调用约定和返回值详情

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

解析go语言调用约定多返回值实现原理

目录go简单代码反汇编go语言调用约定分析1.C/C++调用约定类别2.go语言调用约定go语言如何实现多返回值的总结go简单代码反汇编 用简单的代码用以分析go的调用约定及多返回值的返回方式。 package main func vals
2022-06-07

go语言调用约定多返回值实现原理是什么

这篇文章主要介绍了go语言调用约定多返回值实现原理是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇go语言调用约定多返回值实现原理是什么文章都会有所收获,下面我们一起来看看吧。go简单代码反汇编用简单的代码
2023-06-30

C++ 函数调用函数指针:参数传递和返回值的回调机制

回答: 函数指针允许在运行时动态地调用函数,实现回调机制。参数传递: 函数指针可以作为参数传递给高阶函数,高阶函数对其进行调用并返回结果。返回值的回调机制: 函数可以返回函数指针,实现回调机制,使得一个函数可以作为另一个函数的返回值被传递。
C++ 函数调用函数指针:参数传递和返回值的回调机制
2024-05-02

C++ 函数调用详解:返回值处理的艺术

c++++ 函数调用中的返回值处理涉及:返回值类型:定义返回数据的类型,包括原始类型和引用类型。返回值语义:决定函数如何处理返回值,包括值传递(副本返回)和引用传递(返回引用)。实战案例:说明值传递和引用传递的使用场景和实现方式。C++ 函
C++ 函数调用详解:返回值处理的艺术
2024-05-03

C++ 函数调用异常处理:参数传递和返回值中的异常情况

摘要:函数调用中的异常处理涉及:参数传递异常:传递的参数引发异常时,异常传递给调用函数。返回值异常:函数通过返回值引发异常,直接传递给调用函数。实战示例:myclass::func() 函数参数为空字符串时抛出 std::invalid_a
C++ 函数调用异常处理:参数传递和返回值中的异常情况
2024-05-02

C++ 函数调用 Lambda 表达式:参数传递和返回值的回调优化

在 c++++ 中,可以使用 lambda 表达式作为函数参数,实现回调函数的灵活性。具体而言:参数传递:通过 std::function 包装 lambda 表达式,以函数指针形式传递给函数。返回值处理:使用 std::function
C++ 函数调用 Lambda 表达式:参数传递和返回值的回调优化
2024-05-03

C语言三种函数调用约定_cdecl与_stdcall及_fastcall详细讲解

本篇文章使用的工具是vs2010,内容可能涉及到汇编的知识,建议有一些汇编基础的再来看,不过没有汇编基础也没有关系,了解一下这三种调用约定即可
2022-11-13

C++ 函数调用性能调优:参数传递和返回值的影响

c++++ 函数调用性能优化包括两方面:参数传递策略和返回值类型优化。参数传递方面,传递值适合小对象、不可修改参数,传递引用或指针则适合大对象、可修改参数,而传递指针速度最快。返回值优化方面,小型值可直接返回,大对象应返回引用或指针。选择合
C++ 函数调用性能调优:参数传递和返回值的影响
2024-05-04

C++ 函数调用安全:避免参数错误和返回值陷阱

在 c++++ 中调用函数时,为避免参数错误和返回值陷阱,需要遵循以下步骤:使用类型安全的参数类型并进行范围检查,以避免参数错误。使用错误返回代码并正确处理错误,以避免返回值陷阱。确保函数原型和调用与参数类型和返回值一致。使用调试工具检测参
C++ 函数调用安全:避免参数错误和返回值陷阱
2024-05-03

C语言中getchar函数详解看这一篇就够了(函数功能、使用、返回值)

getchar读取字符的函数,今天通过本文给大家介绍C语言中getchar函数简介用法示例详解,感兴趣的朋友跟随小编一起看看吧
2023-02-28

C++ 函数调用调试技巧:参数传递和返回值的跟踪与调试

c++++ 函数调用调试技巧:参数传递:gdb 使用 call 命令,lldb 使用 expression 命令。返回值:gdb 使用 print 命令,lldb 使用 expr 命令。C++ 函数调用调试技巧:参数传递和返回值的跟踪与调试
C++ 函数调用调试技巧:参数传递和返回值的跟踪与调试
2024-05-05

C++ 函数调用优化:参数传递和返回值高效策略

c++++函数调用优化策略包括:1.参数传递:按引用传递以修改原值;2.const参数:防止参数修改,避免复制;3.返回值:按引用返回大型数据结构,节省拷贝开销;4.返回rvalue引用,避免不必要的复制。通过这些策略,可以优化函数调用性能
C++ 函数调用优化:参数传递和返回值高效策略
2024-04-30

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录