QQ登录

只需要一步,快速开始

APP扫码登录

只需要一步,快速开始

查看: 5293|回复: 0

[C/C++/Qt] 函数参数压栈

[复制链接]

等级头衔

积分成就    金币 : 2861
   泡泡 : 1516
   精华 : 6 篇
   在线时间 : 1330 小时
   最后登录 : 2026-10-31

丰功伟绩

优秀达人突出贡献荣誉管理论坛元老活跃会员

联系方式
发表于 2024-12-2 19:38:26 | 显示全部楼层 |阅读模式
在C++编程中,当函数被调用时,其参数需要通过某种机制传递给函数体内部进行处理。在大多数现代编译器和处理器架构中,这一机制是通过将参数压入调用栈(Call Stack)来实现的。这一过程被称为函数参数压栈(Function Argument Pushing)。5 V/ s0 }7 b6 l3 g0 [. c! T+ r
一、函数参数压栈的基本概念: U# }! C' C, q1 C- {& F' j6 D
调用栈(Call Stack):如前所述,调用栈是一个后进先出(LIFO)的数据结构,用于存储函数调用过程中的状态信息,包括局部变量、参数和返回地址等。
& j. h& ~& Z) X/ C8 L, X8 }; g参数压栈:在函数调用时,参数按照某种顺序(通常是逆序,即从右到左,但具体取决于编译器和调用约定)被压入调用栈中。这样,函数内部就可以通过访问栈上的内存位置来获取这些参数。7 A5 _7 T; h# q8 @. ~& E- _
调用约定(Calling Convention):不同的编译器和平台可能采用不同的调用约定,这些约定定义了参数如何传递、谁负责清理栈(调用者还是被调用者)、以及参数和返回值的传递方式等。9 j2 k1 @5 \/ k. I
二、函数参数压栈的过程: V& h6 m( v( J# {* z; d8 q
准备参数:在调用函数之前,调用者会将函数的参数准备好,并按照调用约定的顺序放入某个临时位置(通常是寄存器或栈上)。
8 k1 U8 {. |) D5 P1 {压栈操作:如果参数通过栈传递,调用者会将这些参数按照逆序(或从右到左,取决于调用约定)压入调用栈中。5 b9 ^+ ]1 i  \
调用函数:调用者将控制权转移给被调用函数,同时将返回地址(即调用函数之后应该继续执行的指令地址)也压入栈中。* n+ j2 B: m0 Y; G2 f
函数执行:被调用函数从栈上读取参数,执行函数体内部的代码。
0 n0 i9 \9 {0 w; y: {6 Z2 e1 M& S5 R返回操作:函数执行完毕后,将返回值(如果有)通过某种方式(如寄存器或栈)返回给调用者,并从栈中弹出返回地址,恢复调用者的执行环境。
$ a" N1 F7 X% s( C* B+ D  ~  `清理栈:根据调用约定,调用者或被调用者负责清理栈上的参数(如果通过栈传递)。, m6 X2 h3 I) Z; \$ }: a5 F1 e  i
三、C++代码示例与压栈过程分析
' W) t, q6 z' {以下是一个简单的C++函数调用示例,用于说明参数压栈的过程:+ M, w" ]  X2 s& p: B
#include <iostream>

void foo(int a, double b, char c) {
    std::cout << "a = " << a << ", b = " << b << ", c = " << c << std::endl;
}

int main() {
    int x = 10;
    double y = 20.5;
    char z = 'z';
    foo(x, y, z); // 调用函数foo,并传递参数
    return 0;
}
在这个例子中,main函数调用了foo函数,并传递了三个参数:x(类型为int),y(类型为double),和z(类型为char)。
7 @" m4 h1 U$ w* _& w2 P假设我们使用的是一种典型的调用约定(如x86架构上的cdecl),参数可能会按照以下顺序被压入栈中:
; M7 }; B6 F8 f
  • z(char类型,通常占用1个字节)首先被压入栈中,尽管它实际上可能是以整数大小(如4个字节)压入的,但只使用了最低有效字节。
  • y(double类型,通常占用8个字节)随后被压入栈中,紧跟在z的后面。
  • x(int类型,通常占用4个字节)最后被压入栈中,位于y的后面。
    & L% {- d9 y: j  s% b4 A+ ~
因此,在foo函数被调用时,栈上的布局可能类似于以下形式(从高到低地址):
|        Return Address           |
|------------------------------|
|             x (int)                      |
|------------------------------|
|             y (double)              |
|------------------------------|
|             z (char)                   |
|------------------------------|
|       (其他栈帧或数据)           |
请注意,这个布局是概念性的,并且实际的内存布局可能因编译器优化、调用约定和平台差异而有所不同。
+ a2 w, @1 L, B- y1 }' p当foo函数开始执行时,它会从栈上读取这些参数,并按照它们在函数声明中的顺序进行处理。) J' D5 y! l$ Z7 x- V" s
四、总结3 h9 W* o& g: T3 o0 `$ ~9 \
函数参数压栈是C++(以及许多其他编程语言)中函数调用机制的核心部分。通过了解参数压栈的过程和调用约定的细节,我们可以更好地理解和优化代码的性能,以及处理与函数调用相关的低级错误(如栈溢出)。然而,需要注意的是,随着编译器和处理器架构的不断发展,实际的参数传递机制可能会发生变化,因此最好参考特定编译器和平台的文档来获取最准确的信息。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|paopaomj.COM ( 渝ICP备18007172号|渝公网安备50010502503914号 )

GMT+8, 2026-11-2 09:39

Powered by paopaomj X3.5 © 2016-2025 sitemap

快速回复 返回顶部 返回列表