精华内容
下载资源
问答
  • 浅析函数的调用过程

    万次阅读 多人点赞 2018-04-19 04:21:18
    函数的调用过程,栈帧的创建和销毁 在c语言中到某一个函数时,它就会跳转过去执行这个函数,执行完毕后接着再去执行下一条指令。在执行调用函数的过程中,计算机通常还要根据函数完成一些工作,这些操作通过形成...

    函数的调用过程,栈帧的创建和销毁


    在c语言中到某一个函数时,它就会跳转过去执行这个函数,执行完毕后接着再去执行下一条指令。在执行调用函数的过程中,计算机通常还要根据函数完成一些工作,这些操作通过形成一个栈帧来完成。栈帧是编译器用来实现函数调用过程的一种数据结构。C语言中,每个栈帧对应着一个未运行完的函数。

    以Add()函数为例研究一下函数的调用过程。

    #include<stdio.h>
    #include<stdlib.h>
    int Add(int a, int b){
        int z = 0;
        z = a + b;
        return z;
    }
    int main(){
        int a = 10;
        int b = 20;
        int ret;
        ret = Add(a, b);
        printf("%d", ret);
        system("pause");
        return 0;
    }

    当讲程序调试的时候,按F10进入调试-窗口-调用堆栈

    调用main函数之前在VC6.0编辑器可以看到main函数在_tmainCRTStartup 函数中调用的,而 _tmainCRTStartup 函数是在 mainCRTStartup 被调用的。这个过程要为函数开辟栈空间, 这块栈空间我们称之为函数栈帧。

    这里写图片描述
    栈帧的需要ebp和esp两个寄存器。 在函数调用的过程中这两个寄存器存放了维护这个栈的栈底和栈顶指针
    注意:ebp指向当前位于系统栈最上边一个栈帧的底部,而不是系统栈的底部。严格说来,“栈帧底部”和“栈底”是不同的概念;ESP所指的栈帧顶部和系统栈的顶部是同一个位置。

    一.开始调用main函数:

    要展开main函数的调用就得为main函数创建栈帧,转到反汇编可以看到过程:

    这里写图片描述

    执行上图第一条指令:
    这里写图片描述
    1.压栈,把ebp放入栈顶,而esp始终指向栈顶
    2.将esp值传给ebp,也就是让esp,ebp移在一起
    3.sub为减的意思,即将esp-0E4h赋给esp,且函数调用分配由高地址向低地址增长,因此esp向上移动,即开辟了新空间,也就是为main函数开辟空间
    4.三个push压榨分别将ebx,esi,edi按顺序压入栈顶,而esp也会指向栈顶
    5.lea指令,加载有效地址;将ebp-0E4h的地址放入edi中,也就是edi指向ebp-0E4h
    把39h放到ecx中
    把0cccccccch放到eax中
    从edi所指向的地址开始向高地址进行拷贝,拷贝的次数为ecx内容,拷贝的内容为eax内

    这里写图片描述
    6.创建变量a与b并初始化10和20.
    这里写图片描述

    二.Add函数的调用

    1.把b放入eax中,然后对eax压栈(形参a)
    2.把a放入ecx中,然后对ecx压栈(形参b)
    3.call作用:将下一条指令地址压栈,然后进入add函数里面
    这里写图片描述
    注意:call语句push的是下一条指令的地址,为了函数返回时知道从哪儿接着执行
    接下来进入add函数:
    A.先把main函数ebp压栈,保存指向main()函数栈帧底部的ebp的地址,目的是当返回时能找到main函数栈底,此时esp指向新的栈顶位置
    将main函数的ebp压栈,也是为了返回时找到main函数栈底
    B.将esp的值赋给ebp,产生新的ebp,即Add()函数栈帧的ebp;
    C.给esp减去一个16进制数0CCh(为Add()函数预开辟空间);
    D.push ebx、esi、edi;
    E.lea指令,加载有效地址;
    F.初始化预开辟的空间为0xcccccccc;
    这里写图片描述
    G.创建变量z并为其赋值
    H.把形参a放到eax,即把10,放入eax把形参b加到eax中,即把20加到eax中再把eax放到z的位置,即把两数之和放到z中
    I.把z的值放到寄存器eax中返回,因为z为函数临时开辟的变量空间等函数执行完会销毁,因此放寄存器中返回
    K.接下来执行pop出栈操作,edi esi ebx依次从上向下出栈,esp 会向下移动,栈的特点:先进后出,后进先出
    L.将ebp值赋给esp,也就是esp向下移动指向ebp位置,此时add开辟的栈空间已经销毁
    M.pop将栈顶的元素弹出放到ebp中,也就是说将main函数的ebp放入ebp中,即ebp现在指向main函数ebp
    这里写图片描述
    N,在执行ret后,会把之前push的地址弹出去,这时就要返回main函数,这也就是为什么之前要push这个地址,这样call指令就完成了
    接下来从那个call指令继续执行
    这里写图片描述
    O.把esp+8,即esp向下移,把形参销毁

    最后就是对main函数栈帧的销毁,方法同上,就不在赘述。

    栈帧的总结:

    1.堆栈是C语言程序运行时必须的一个记录调用路径和参数的空间:
    函数调用框架;
    传递参数;
    保存返回地址;
    提供局部变量空间;

    2. 堆栈寄存器和堆栈操作
    堆栈相关的寄存器
    esp,堆栈指针(stack pointer)
    ebp,基址指针(base pointer)
    堆栈操作
    push 栈顶地址减少4个字节(32位)
    pop 栈顶地址增加4个字节
    ebp在C语言中用作记录当前函数调用基址

    展开全文
  • C++构造函数和析构函数的调用顺序

    千次阅读 2018-09-04 00:05:03
    1、构造函数的调用顺序  基类构造函数、对象成员构造函数、派生类本身的构造函数  2、析构函数的调用顺序 派生类本身的析构函数、对象成员析构函数、基类析构函数(与构造顺序正好相反)  3、特例  局部...

    1、构造函数的调用顺序 

    基类构造函数、对象成员构造函数、派生类本身的构造函数  

    2、析构函数的调用顺序

    派生类本身的析构函数、对象成员析构函数、基类析构函数(与构造顺序正好相反) 

    3、特例 

    局部对象,在退出程序块时析构

    静态对象,在定义所在文件结束时析构

    全局对象,在程序结束时析构 

    继承对象,先析构派生类,再析构父类 

    对象成员,先析构类对象,再析构对象成员

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    
    class  Clock
    {
    public:
    	Clock()
    	{
    		cout << "clock的构造函数" << ",";
    	}
    	~Clock()
    	{
    		cout << "clock的析构函数" << ",";
    	}
    };
    
    class Date
    {
    public:
    	Date()
    	{
    		cout << "date的构造函数" << ",";
    	}
    	~Date()
    	{
    		cout << "date的析构函数" << ",";
    	}
    };
    
    
    void main()
    {
        Clock c;
    	Date d;
    	system("pause");
    }

    在使用构造函数和析构函数时,需要特别注意对它们的调用时间和调用顺序。在一般情况下,调用析构函数的次序正好与调用构造函数的次序相反:最先被调用的构造函数,其对应的(同一对象中的)析构函数最后被调用,而最后被调用的构造函数,其对应的析构函数最先被调用。
    可以简记为:先构造的后析构,后构造的先析构,它相当于一个栈,先进后出

    展开全文
  • SQL 标量值函数的调用

    千次阅读 2018-11-10 09:19:35
    SQL 标量值函数的调用

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

    也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

                   

    调用 MS SQL 标量值函数,应该在函数前面加上 "dbo.",否则会报 “不是可以识别的 内置函数名称”错误。例如

     

        DECLARE @WhichDB TINYINT;
        SELECT @WhichDB = dbo.user_GetWhichDB(1);--看看是哪个数据库的

     

    另外,标量值函数就相当于一个变量,而不是一个,所以这样写是错误的:

        SELECT * FROM dbo.user_GetWhichDB(1);

    应该这样写:

        SELECT dbo.user_GetWhichDB(1);

    加上别名:

        SELECT dbo.user_GetWhichDB(1) AS FieldName;

     

    =================================================

    --标量值函数

    ALTER FUNCTION [dbo].[user_GetWhichDB]
    (
        @UserId INT = 0
    )
    RETURNS TINYINT
    WITH EXECUTE AS CALLER
    AS
    BEGIN
        DECLARE @WhichDB TINYINT;
        SET @WhichDB = 1;
        IF @UserId >= 115098
            SET @WhichDB = 2;
       
        RETURN (@WhichDB);
    END

               

    给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

    这里写图片描述
    展开全文
  • c语言函数的调用

    千次阅读 2019-06-03 09:15:42
    /* 1. 函数三要素 函数名 形参列表 函数体 2. 如何调用函数? 函数名(); 3.函数体内容书写一定要谨慎 ... 函数哪里需要哪里都可以调用 ... 声明一个函数 求两个数和 */ #include <stdio...

    /*
       1. 函数三要素 
          函数名 形参列表   函数体
       2. 如何调用函数?
        函数名();
        
        3.函数体内容书写一定要谨慎 
        
        
        bug  是编程常用  出现了问题 
        
        
        函数哪里需要哪里都可以调用 
        
        声明一个函数  求两个数的和 
     

    */ 
    #include <stdio.h> 
    add(int a,int  b){
       
        printf("%d \n",a+b); 
        
        minus(a,b); 
        
    }
    //声明一个求两个数的 相减的函数
    minus(int c,int d){
    
      printf("%d",c-d); 
      
      //我把c-d数值传递进去 
      begSquareArea(c-d); //-1 
    } 
    begSquareArea(int a){//-1 
    
      if(a<0){
        printf("边长没有负数"); 
      } else{
        printf("长方形的面积%d",a*a); //1 
    } 
      } 
    
    
    
    
    main(){
    
      add(4,3); 
      /*minus(3,2); */ 
    } 
    
    

     

    //函数自己调用自己 递归调用
    //比如打印1-100 
    //递归调用的时候 如果你不手动中止  函数会一直调用自己 

    #include <stdio.h> 
    add(int a,int  b){
       
        printf("%d \n",a+b); 
        
       add(a,b); 
    } 
    ontToBai(int a){
    
      a++;
      printf("%d \n",a); //1
      
      
      if(a==100){
      
         //break  不能中止 
      } else{
         ontToBai(a); 
      } 
    } 
    
    main(){
    
     // add(4,3); 
     ontToBai(0); 
    
    } 

     

    展开全文
  • 函数的调用语法 在Makefile中可以使用函数来处理变量,从而让我们的命令或是规则更为的灵活和具有智能。make所支持的函数也不算很多,不过已经足够我们的操作了。函数调用后,函数的返回值可以当做变量来使用。 ...
  • fortran 函数的调用标准

    千次阅读 2015-06-23 11:38:00
    Fortran函数的调用标准在编译时使用iface声明,如iface:default,表示采用的是default标准。 fortran的调用标准有 [1] default: Tells the compiler to use the default calling conventions. [2] cref: Tells ...
  • Python:几种函数的调用方式

    万次阅读 2018-06-14 14:46:07
    第一种:class内部def函数与def函数之间的调用test_getIdentify 函数调用test_getLastuser函数的返回值,只要在test_getIdentify 函数内写上self.test_getLastuser()即可class testlogin(unittest.TestCase): ...
  • 下面本文将详细介绍java语言中对象的创建过程,以及函数的调用过程。 class Person{ public String name="zhao"; public int ege; public static String country = "CN"; Person(String name,int ege){ this....
  • IDEA查看函数的调用和被调用关系

    千次阅读 2020-03-07 20:01:51
    一问题提出 查看doLogin函数调用调用关系。 二查看被调用关系 1选择dologin函数 2CTRL+ALT+H 三 查看调用关系
  • 微信小程序云开发之云函数的调用

    千次阅读 2019-08-27 17:56:59
    本篇讲述微信小程序云开发之云函数的调用 下面是小程序云函数的根文件夹 如果我们想要查看当前开发云函数的使用情况,直接在我们的云开发控制台进行查看即可 新建云函数: 鼠标右键点击cloudfunctions|test 点击...
  • C++继承中父类构造函数和析构函数的调用顺序
  • c++构造函数和析构函数的调用顺序

    千次阅读 2019-04-22 14:33:45
    在使用构造函数和析构函数时,需要特别注意对他们的调用时间和调用顺序,在一般情况下,调用析构函数的次序正好与调用构造函数的次序相反,最先被调用的构造函数,其对应(同一对象中的)析构函数最后被调用,而最后...
  • 首先为该函数打断点,打完断点后程序运行直到断点处,在gdb中输入命令bt即可打出断点函数的调用栈,从上到下理清该函数的完整调用流程,个人今天觉着这功能挺牛逼强大,分享一下便于日后查阅。   后面内容为转载...
  • 首先请看下面的语句: Point3d obj; Point3d *ptr = &obj; 当使用上述指针或者对象调用成员函数Func()时,会有: ...假设Func函数的定义如下: Point3d Point3d::Func() const { Float a = getA();
  •  先看看构造函数的调用顺序规则,只要我们在平时编程的时候遵守这种约定,任何关于构造函数的调用问题都能解决;构造函数的调用顺序总是如下: 1.基类构造函数。如果有多个基类,则构造函数的调用顺序是某类在类...
  • shell脚本中函数的调用

    万次阅读 2018-05-09 14:48:45
    函数 : 把一个功能封装起来,使用时直接调用函数名,这样的脚本好处:模块化,代码可读性强,扩展性方便函数的定义 在shell 中有两种定义函数的语法格式,分别为: 函数名() { 命令序列 } 或者: function...
  • 一、五种函数调用方法(不够准确、全面,但初学者可以先这样理解):一是自建函数位于builtins模块中的函数,可直接调用。具体有那些函数?可通过dir(__builtins__)查看,共计152个(‘len(dir(__builtins__))’)。...
  • 纯虚函数的调用

    千次阅读 2017-11-04 09:16:29
    纯虚函数怎么可以调用呢?看下面这个例子class Abstract_base { public: virtual ~Abstract_base(); virtual void interface() = 0; const char* mumble() const { return _mumble;} protected: char *_
  • c++:(各种)构造函数的调用方式

    万次阅读 多人点赞 2017-03-08 13:07:09
    这篇博客主要记录这3个构造函数、1个赋值函数的调用方式,希望大家学习之后,不但知道如何调用,还可以根据一句话来判断到底调用了几次构造函数。可以通过一个例子来说明,假如我现在有一个Anima
  • 解决:对 PInvoke 函数的调用导致堆栈不对称问题 问题描述:  在使用托管代码调用非托管代码时,发生“对 PInvoke 函数“UseTwiHikVisionDllTest!UseTwiHikVisionDllTest.TwiHikVision::GetFirstPic”的...
  • 参考3: C++构造函数与析构函数的调用顺序 2.构造函数、析构函数与拷贝构造函数介绍 2.1构造函数 构造函数不能有返回值缺省构造函数时,系统将自动调用该缺省构造函数初始化对象,缺省构造函数会将所有...
  • 显示函数的调用关系是调试器的必备功能,如果我们在程序的运行中出现了崩溃的情况,通过函数的调用关系可以快速定位问题的根源,懂得函数调用关系的实现原理也可以扩充自己的知识面,在没有调试器的情况下,我们也...
  • 对PInvoke函数的调用导致堆栈不对称问题
  • c#的构造函数及构造函数的调用

    千次阅读 2015-12-01 16:53:07
    C#构造函数的特性一、 什么是C#构造函数?Construct,Function  C#构造函数是一种特殊的成员函数,它主要用于为对象分配存储空间,对数据成员进行初始化.  C#构造函数具有一些特殊的性质:  (1)C#构造...
  • JS中函数内套函数的调用

    万次阅读 2015-04-03 15:16:50
    那么有两种方法调用里面嵌套的函数 function f1() { var n = 99; function f2() { alert(n); } return f2; //因为返回了f2,所以调用f1()时,返回函数f2 } 1,把函数赋值f1给变量,那么调用变量时相当于...
  • C语言自定义函数的调用

    千次阅读 2019-09-25 22:48:43
    题目:求an次方-bn次方 #include<stdio.h> int pow(int begin,int end); int main(void) { int a,b,n,sum1,sum2; scanf("%d %d %d",&...//int pow中return会返回一个值给调用 int pow...
  • Unity中Awake与Start函数的调用情况总结

    万次阅读 多人点赞 2014-03-23 08:00:03
    Unity中Awake与Start函数的调用情况总结  在Unity中编写脚本时,有一系列的可重写(override)函数供我们使用,其中的Awake与Start两个函数作为初始化与设置之用,几乎在每个脚本中都要用到。因此,正确的把握这两...
  • js关于有参函数的调用

    千次阅读 2013-01-22 17:59:57
    学了这么久的js居然没有意识到这个问题。 对于一个无参函数,调用很简单,如下: var btn=document.getElementById("btn");...所以,我理所当然地认为,有参函数的调用,如下: var btn=document.getElem
  • 类的对重载函数的调用不明确

    千次阅读 2013-07-28 17:19:01
    今天犯了一个错误: HMenu(QWidget* _parent=0); HMenu(QString _txt = " ", QWidget*...error C2668: “HMenu::HMenu”: 对重载函数的调用不明确 f:\workspace\myclasslibraries\hgui\hgui\src\HMenu.h 在网上搜都
  • VS报错 error C2668: “sqrt”: 对重载函数的调用不明确的解决办法 错误描述 error C2668: “sqrt”: 对重载函数的调用不明确 可能是“long double sqrt(long double)” 或 “float sqrt(float)” ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 481,554
精华内容 192,621
关键字:

函数的调用