C++: How is the process of function calling in C++

C++: How is the process of function calling in C++

In general, the process of calling function is put the parameters into
the stack, move the EBP and ESP (jump into another function), protect
the information of father function, restore the information of father
function. (参数入栈、函数跳转、保护现场、回复现场)

There are three special pointer in compiler:EIP(Instruction
pointer)是指令指针,point to the address of next instrction
(即指向下一条即将执行的指令的地址);EBP (bottom pointer)
为基址指针,point to the bottom of stack 常用来指向栈底;ESP (stack
pointer) 为栈指针,point to the top of stack 常用来指向栈顶.

For example, here is the code:

图片 1

First, we are at the main function. Every function has itsEBX (basic
register),ESI,EDI (destination
register)分别为基址寄存器,源变址寄存器,目的变址寄存器. They are some
important information of a particular function. When we call the
g_func, we firstly need to transport the parameters into it. As I
mentioned in my C++ note, when we are using the transportation of value,
we will create copies of the parameters for the function. Therefore,
here is the first step: put the parameters into the function stack.

三条push指令,分别将三个参数压入栈中,可以发现参数的压栈顺序是从右向左的。这时我们可以查看栈中的数据验证一下。如图3所示,从右边的实时寄存器表中我们可以看到ESP(栈顶指针)值为0x0012FEF0,然后从中间的内存表中找到内存地址0x0012FEF0处,我们可以看到内存中依次存储了0x00000001(即参数1),0x00000002(即参数2),0x00000003(即参数3),即此时栈顶存储的是三个参数值,说明压栈成功。

图片 2

If we translate the code into instructions, we will get:

图片 3

 

We firstly push three parameters into stack. Then we call a instruction
at the address of 00401005. Following this address, we can see this:

图片 4

We can see this is a jump instruction at the instruction address of

  1. It jumps to 00401030. That is the begin instruction address of
    g_func.

 

Until now, we see how the instructions like before entering the
instructions of function. Firstly push the parameters into stack, then
use a call instruction to a jump instruction to jump to the actual
instruction address of the function. So, until now, the stack is like
above. It has three parameters in it now.

 

Now, we turn to protect information before really entering function.

 

This step consists of three actual movement:

1)第一步的隐含的,并没有显式的指令去指挥其完成。就是将之前的call
指令之后EIP地址(main
函数的下一个指令的地址)压进去栈中。在这个例子中,call之后则为add。地址是00401093.

图片 5

 

2) ebp is the original bottom pointer of the main function.
变了函数的话,栈底都是要变化的。我们要保存之前的函数的栈底地址,作为保护现场的第一步.
所以第二步就是将之前的函数的EBP压进栈中。

下一条mov ebp, esp
将此时的栈顶地址作为该函数的栈基址,确定g_func函数的栈区域(ebp为栈底,esp为栈顶)。再往下的指令是sub
esp, 48h,指令的字面意思是将栈顶指针往上移动48h
Byte。那为什么要移动呢?这中间的内存区域用来做什么呢?这个区域为间隔空间,将两个函数的栈区域隔开一段距离,如图7所示。而该间隔区域的大小固定为40h,即64Byte,然后还要预留出存储局部变量的内存区域。g_func函数有两个局部变量x和y,所以esp需移动的长度为40h+8=48h。

 

注意,存放局部变量的区域是64Bytes另外移出来的。并不会占用64Byte的空间。确保至少函数的栈空间之间至少有64Bytes的间隔。

移动之后的栈空间分布如图:

图片 6

 

3) 将之前提到的EBX (basic register),ESI,EDI (destination
register)分别为基址寄存器,源变址寄存器,目的变址寄存器压进去当前ESP的上面的地址。作为保存现场的第三步。

 

此时我们可以知道,栈内存是这样的:

 

图片 7

 

The following content is talking about how to restore the information, I
just directly copied form

 

图片 8图片 9

图片 10

: How is the process of function calling in
C++ In general, the process of calling function is put the parameters
into the stack, move the EBP and ESP (jump into another
functio…

除了必要的InitializeSecurityDescriptor和SetSecurityDescriptorDacl,

function是一组函数对象包装类的模板,实现了一个泛型的回调机制。

没有使用virtual的情况:

  1. To enable searching within windows SharePoing Services, the full-text
    searching feature for SQL Server 2000 must be installed on the SQL
    Server computer;

  2. When SQL Server 2000 has been configured to support full-text
    searching, windows SharePoint Services can be configured to enable
    search.

  3. Please operate the SharePoint Server according to the following
    prcedures.
    (1) Click Site Settings.
    (2) Click Go to SharePoint Portal Server Central Administration.
    (3) In the links to related administration home pages, click on Windows
    SharePoint Services
    (4) Click on Configure Full-Text Search in the component configuration
    section
    (5) In the Search Settings section, click on Enable Full-Text Search and
    Index Component to select the service

内存映射文件名必须GLOBAL开头

引入头文件

#include <iostream>

using namespace std;
class Parent {

public:
    void show() {

        cout << "I'm parent" << endl;
    }
};

class child :public Parent {
public:
    void show() {
        cout << "I'm child" << endl;
    }
};

int main() {
    Parent *ptr = new child();
    ptr->show();
    system("pause");
    return 0;
}

In addition, the following codes will be used to show a search form on
the aspx page.
<SharePoint:ViewSearchForm ID=”L_SearchView” Prompt=”Search this
site” Go=”Go” Action=”searchresults.aspx” runat=”server”/>

#include <functional>
using namespace std;
using namespace std::placeholders;  //bind的时候会用`

图片 11

参考:

OutputParent

fuction  bind:

think different,change world

我们可以调用的对象有很多,比如普通函数、函数指针、lanmbda表达式、函数对象和类的成员函数等。

当在base类中使用了virtual
function时,我们就能访问用base类对象的指针,访问派生类的方法了。看下面:

不管采用哪种方式,主要调用形式一样(返回值类型、传递给调用的实参类型),我们就可以使用同一种形式来调用。

#include <iostream>

using namespace std;
class Parent {

public:
    virtual void show() {

        cout << "I'm parent" << endl;
    }
};

class child :public Parent {
public:
    void show() {
        cout << "I'm child" << endl;
    }
};

int main() {
    Parent *parent_object_ptr = new child();
    parent_object_ptr->show();
    system("pause");
    return 0;
}

这个时候就可以用到function模板,它给予我们在调用的方式上更大的弹性。

图片 12

请看一下三种不同的函数定义:

virtual function

 

[cpp] view
plain copy

 

 print?

  1. int add(int a, int b){  
  2.     return a+b;  
  3. }  
  4. auto mod=[](int a, int b){return a%b;};  
  5. struct divide{  
  6.     int operator()(int m, int n){  
  7.         return m/n;  
  8.     }  
  9. };  

 

这三种都可以使用同一种调用形式,int(int, int),调用方式如下:

 

[cpp] view
plain copy

 

 print?

  1. function<int(int,int)> func1= add;  
  2. function<int(int,int)> func2= divide();  
  3. function<int(int,int)> func3= mod;  
  4. cout<<func1(5, 6)<<endl;  
  5. cout<<func2(5, 6)<<endl;  
  6. cout<<func3(5, 6)<<endl;  

学会了使用function,可以继续如下进行抽象定义,不同类型采用相同的调用方法:

 

 

[cpp] view
plain copy

 

 print?

  1. map<string,function<int(int, int)>> funs =  
  2. {  
  3.     {“+”, add},  
  4.     {“-“, std::minus<int>()},//标准库的函数,参数为两个整数,可以参考前一篇博客  
  5.     {“/”, divide()},//类成员函数  
  6.     {“*”, [](int i,int j){return i*j;}},//lambda表达式  
  7.     {“%”, mod},  
  8. };  
  9. funs[“+”](4,6);  

以上就是function的简单使用。下面是从另一篇博客转载的,使用function的引用来保存函数对象。考虑下面代码:

 

 

[cpp] view
plain copy

 

 print?

  1. class CAdd  
  2. {  
  3. public:  
  4.     CAdd():m_nSum(0){NULL;}  
  5.     int operator()(int i)  
  6.     {  
  7.         m_nSum += i;  
  8.         return m_nSum;  
  9.     }  
  10.       
  11.     int Sum() const  
  12.     {  
  13.         return m_nSum;  
  14.     }  
  15.       
  16. private:  
  17.     int m_nSum;  
  18. };  
  19. int main(int argc, const char * argv[])  
  20. {  
  21.     CAdd cAdd;  
  22.     function<int(int)> funcAdd1 = cAdd;  
  23.     function<int(int)> funcAdd2 = cAdd;  
  24.     cout<<funcAdd1(10)<<endl;  
  25.     cout<<funcAdd2(10)<<endl;  
  26.     cout<<cAdd.Sum()<<endl;  
  27.       
  28.      return 0;  
  29. }  

 

上面的输出结果是 10 10
0。我们将同一个函数对象赋值给了两个function,然后分别调用这两个function,但函数中的成员变量的值没有保存,问题在哪里?因为function的缺省行为是拷贝一份传递给它的函数对象,于是f1,f2中保存的都是cAdd对象的拷贝。

C++11提供了ref和cref函数来提供对象的引用和常引用的包装。要是function能够正确保存函数对象的状态,可以如下修改代码:

 

[cpp] view
plain copy

 

 print?

  1. function<int(int)> funcAdd3 = ref(cAdd);  
  2. function<int(int)> funcAdd4 = ref(cAdd);  
  3. cout<<funcAdd3(10)<<endl;  
  4. cout<<funcAdd4(10)<<endl;  
  5. cout<<cAdd.Sum()<<endl;  

 

 

       
另外,两个function之间赋值时,如果源function保存的是函数对象的拷贝,则目标function保存的也是函数对象的拷贝。如果源function保存的是对函数对象的引用,则目标function保存的也是函数对象的引用。

相关文章