函数栈帧的调用

c 语言的构成就是函数 现在写一写心得和体会

在函数中毫无疑问 第一个函数无疑是创世之作 一般来说刚刚学习c语言的适合main 函数无疑是遇见的第一个函数那么就结合main 函数的初始化 以及如何用汇编调用main 函数写一篇心得

#include <stdio.h>
int main()
{
010E1810  push        ebp  
010E1811  mov         ebp,esp  
010E1813  sub         esp,0C0h  
010E1819  push        ebx  
010E181A  push        esi  
010E181B  push        edi  
010E181C  lea         edi,[ebp-0C0h]  
010E1822  mov         ecx,30h  
010E1827  mov         eax,0CCCCCCCCh  
010E182C  rep stos    dword ptr es:[edi]  
	printf("hello\n");
010E182E  push        offset string "hello\n" (010E6B30h)  
010E1833  call        _printf (010E1320h)  
010E1838  add         esp,4  
	return 0;
010E183B  xor         eax,eax  
}
010E183D  pop         edi  
010E183E  pop         esi  
010E183F  pop         ebx  
010E1840  add         esp,0C0h  
010E1846  cmp         ebp,esp  
010E1848  call        __RTC_CheckEsp (010E1113h)  
010E184D  mov         esp,ebp  
010E184F  pop         ebp  
010E1850  ret  

这是一段汇编代码 先解释如何使用栈 

首先先要清楚 每次push esp 都会增加

也就是这个 


这个是main 函数刚刚进的时候的图 

接下来分析 另外一个代码 

int main()
{
00EF1740  push        ebp  
00EF1741  mov         ebp,esp  
00EF1743  sub         esp,0E4h  
00EF1749  push        ebx  
00EF174A  push        esi  
00EF174B  push        edi  
00EF174C  lea         edi,[ebp-0E4h]  
00EF1752  mov         ecx,39h  
00EF1757  mov         eax,0CCCCCCCCh  
00EF175C  rep stos    dword ptr es:[edi]  
	int a =20;
00EF175E  mov         dword ptr [a],14h  
	int b = a;
00EF1765  mov         eax,dword ptr [a]  
00EF1768  mov         dword ptr [b],eax  
	int c=add(10, 10);
00EF176B  push        0Ah  
00EF176D  push        0Ah  
00EF176F  call        add (0EF126Ch)  
00EF1774  add         esp,8  
00EF1777  mov         dword ptr [c],eax  
	return 0;
00EF177A  xor         eax,eax  
}
00EF177C  pop         edi  
00EF177D  pop         esi  
00EF177E  pop         ebx  
00EF177F  add         esp,0E4h  
00EF1785  cmp         ebp,esp  
00EF1787  call        __RTC_CheckEsp (0EF110Eh)  
00EF178C  mov         esp,ebp  
00EF178E  pop         ebp  
00EF178F  ret  
前面就不说了 就看函数调用add(10,10)

同时我们为了通俗易懂 我们把ebp寄存器里现有的值就叫 手上edp 把之前的值叫做 老edp

如何执行 先看内存 


接下来验证是否一致 进入函数后

入口地址刚好是由于是小端 低地址放低位 即

00ef1774 即 

00EF1700  push        ebp  
00EF1701  mov         ebp,esp  
00EF1703  sub         esp,0C0h  
00EF1709  push        ebx  
00EF170A  push        esi  
00EF170B  push        edi  
00EF170C  lea         edi,[ebp-0C0h]  
00EF1712  mov         ecx,30h  
00EF1717  mov         eax,0CCCCCCCCh  
00EF171C  rep stos    dword ptr es:[edi]  
	return a + b;
00EF171E  mov         eax,dword ptr [a]  
00EF1721  add         eax,dword ptr [b]  
}
00EF1724  pop         edi  
00EF1725  pop         esi  
00EF1726  pop         ebx  
00EF1727  mov         esp,ebp  
00EF1729  pop         ebp  
00EF172A  ret  

执行完之后 

 push        ebp  

20 f9 93 00  
0x0093F824  74 17 ef 00

将手中的edp=93f920和图中测试一样 压入栈得出 

同时接下来使得edp=esp 这样手上又有了edp得副本 很容易还原函数

这样下来规律自然体现

因此这个图可以完善了

程序员自我修养上 

图是这样得



我认为确切得从内存上来看是这样得


这个图是有一点点和常规不一样 这样是从后面地址得出前面的值

也就是 *手上的edp 可以得出的是老edp 

总而言之 栈的进和出也已经差不多了 我没有写返回值 因为有的函数没有返回地址 其实返回int类型什么的都是通过eax寄存器带出来的 所以也就没有写明

接下来就是程序的入口地址 

其实也有说明 我们不断执行下一步

static void __cdecl pre_cpp_initialization() throw()
{
    // Before we begin C++ initialization, set the unhandled exception
    // filter so that unhandled C++ exceptions result in std::terminate
    // being called:
    __scrt_set_unhandled_exception_filter();

    _set_new_mode(_get_startup_new_mode());
}

// When both the PGO instrumentation library and the CRT are statically linked,
// PGO will initialize itself in XIAB.  We do most pre-C initialization before
// PGO is initialized, but defer some initialization steps to after.  See the
// commentary in post_pgo_initialization for details.
_CRTALLOC(".CRT$XIAA") static _PIFV pre_c_initializer    = pre_c_initialization;
_CRTALLOC(".CRT$XIAC") static _PIFV post_pgo_initializer = post_pgo_initialization;
_CRTALLOC(".CRT$XCAA") static _PVFV pre_cpp_initializer  = pre_cpp_initialization;



static __declspec(noinline) int __cdecl __scrt_common_main_seh() throw()
{
    if (!__scrt_initialize_crt(__scrt_module_type::exe))
        __scrt_fastfail(FAST_FAIL_FATAL_APP_EXIT);

    bool has_cctor = false;
    __try
    {
        bool const is_nested = __scrt_acquire_startup_lock();

        if (__scrt_current_native_startup_state == __scrt_native_startup_state::initializing)
        {
            __scrt_fastfail(FAST_FAIL_FATAL_APP_EXIT);
        }
        else if (__scrt_current_native_startup_state == __scrt_native_startup_state::uninitialized)
        {
            __scrt_current_native_startup_state = __scrt_native_startup_state::initializing;

            if (_initterm_e(__xi_a, __xi_z) != 0)
                return 255;

            _initterm(__xc_a, __xc_z);

            __scrt_current_native_startup_state = __scrt_native_startup_state::initialized;
        }
        else
        {
            has_cctor = true;
        }

        __scrt_release_startup_lock(is_nested);

        // If this module has any dynamically initialized __declspec(thread)
        // variables, then we invoke their initialization for the primary thread
        // used to start the process:
        _tls_callback_type const* const tls_init_callback = __scrt_get_dyn_tls_init_callback();
        if (*tls_init_callback != nullptr && __scrt_is_nonwritable_in_current_image(tls_init_callback))
        {
            (*tls_init_callback)(nullptr, DLL_THREAD_ATTACH, nullptr);
        }

        // If this module has any thread-local destructors, register the
        // callback function with the Unified CRT to run on exit.
        _tls_callback_type const * const tls_dtor_callback = __scrt_get_dyn_tls_dtor_callback();
        if (*tls_dtor_callback != nullptr && __scrt_is_nonwritable_in_current_image(tls_dtor_callback))
        {
            _register_thread_local_exe_atexit_callback(*tls_dtor_callback);
        }

        //
        // Initialization is complete; invoke main...
        //

        int const main_result = invoke_main();

        //
        // main has returned; exit somehow...
        //

        if (!__scrt_is_managed_app())
            exit(main_result);

        if (!has_cctor)
            _cexit();

        // Finally, we terminate the CRT:
        __scrt_uninitialize_crt(true, false);
        return main_result;
    }
如果追寻到底就是

extern "C" int mainCRTStartup()
{
    return __scrt_common_main();
}
在exe.cpp里面

<think>好的,用户想了解函数的概念以及比特鹏哥的相关教程。首先,我需要回忆一下函数的基本知识。函数是在程序运行时为每个函数调用分配的内存区域,用于存储局部变量、参数和返回地址等信息。ESP和EBP寄存器在这个过程中起到维护的作用。 接下来,用户提到了比特鹏哥的教程,可能需要结合他的教学内容来解释。根据引用[1],区的高地址和低地址之间会为main函数开辟,ESP和EBP会随着函数调用维护不同的。例如,调用Add函数时,寄存器会转而维护Add的。这部分内容可以作为基础,说明的动态变化。 然后,我需要考虑如何结构清晰地回答。可能需要分步骤讲解函数的创建、使用和销毁过程,并结合具体的例子,比如函数调用时参数传递、局部变量的存储。引用[2]中的Python函数参数示例虽然与C/C++不同,但可以对比说明中的参数传递机制。不过主要还是要以C/C++为例,因为函数通常是在这类语言中详细讨论的。 此外,要注意用户可能对寄存器ESP和EBP的作用不太清楚,需要解释它们在中的角色。比如,EBP指向的底部,ESP指向顶部,随着数据入而变化。同时,的空间分配、局部变量的布局以及函数返回时的清理过程也是重点。 关于比特鹏哥的教程,可能需要提到他的教学内容是否覆盖了的详细结构、调试方法或者实际应用案例。例如,通过反汇编或调试工具观察的变化,这样可以帮助理解理论概念。 用户可能还有更深层的需求,比如理解溢出原理、递归调用使用情况,或者如何优化空间。这些问题可以作为后续的相关问题提出,引导进一步学习。 最后,确保回答符合系统级指令的要求,正确使用LaTeX格式,生成相关问题,并引用提供的资料。需要检查是否有遗漏的要点,比如与静态方法的关系,但引用[3]提到的是静态方法中this不存在,这可能与中的局部变量表有关,但主要适用于面向对象语言,如Java,而函数更多在过程式语言如C中讨论。因此可能需要区分不同语言的环境。 总结回答结构:定义函数,寄存器作用,创建与销毁过程,比特鹏哥教程内容,相关引用,最后生成问题。</think>### 函数详解 #### 1. 函数的定义与作用 **函数**是程序运行时为每个函数调用分配的独立内存区域,用于存储函数的局部变量、参数、返回地址和临时计算结果等数据[^1]。 - **区结构**: 在内存的区中,高地址存放主函数(如 `main``)的,低地址随着嵌套函数调用逐步分配新- **寄存器维护**: `esp`(顶指针)和 `ebp`(底指针)寄存器动态维护当前函数边界。例如,调用 `Add` 函数时,`ebp` 会指向 `Add` 的底部,`esp` 指向顶部。 #### 2. 函数的创建与销毁过程 以C语言为例,函数的操作分为以下步骤: 1. **参数压**: 调用函数前,参数按从右到左顺序压入中。 2. **保存返回地址**: 将下一条指令的地址存入,供函数返回时使用。 3. **调整寄存器**: - 将当前 `ebp` 的值压保存。 - 将 `ebp` 更新为当前 `esp` 的值,标记新的底部。 - 调整 `esp` 为新分配空间(如预留局部变量空间)。 ```c void Add(int a, int b) { int c = a + b; // 局部变量c存储在Add函数中 } ``` 4. **局部变量存储**: 函数内部定义的变量(如 `c`)占用空间。 5. **销毁**: 函数返回时,`esp` 恢复为 `ebp` 的值,释放空间;`ebp` 恢复为调用前的值,返回到上一级函数[^1]。 #### 3. 比特鹏哥教程重点 比特鹏哥的教程中可能涵盖以下核心内容: - **调试观察**: 通过调试工具(如GDB)查看 `esp` 和 `ebp` 的变化,直观理解分配。 - **汇编分析**: 结合反汇编代码,解析函数调用对应的机器指令(如 `call`、`ret`、`push`、`pop`)。 - **实际案例**: 分析递归调用或嵌套函数时的叠加,解释溢出的原理。 #### 4. 关键知识点对比 | 场景 | 变化 | |--------------------|--------------------------------------------------------------------------| | 函数调用时 | `ebp` 指向新底部,`esp` 指向顶部 | | 函数返回时 | `esp` 回退到调用前的顶,`ebp` 恢复为原底部 | | 多级嵌套调用 | 逐层叠加,形成“增长”(向低地址延伸) |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值