精华内容
下载资源
问答
  • C语言函数调用三种方式:传值调用,引用调用和传地址调用

    我想,你只要看了C语言上关于传值函数调用的测试题,一切都会了然于胸:

    1. 考题一:程序代码如下:
      void Exchg1(int x, int y)
      {
      int tmp;
      tmp=x;
      x=y;
      y=tmp;
      printf(“x=%d,y=%d/n”,x,y)
      }
      void main()
      {
      int a=4,b=6;
      Exchg1 (a,b) ;
      printf(“a=%d,b=%d/n”,a,b)
      }
      输出的结果:
      x=_, y=_
      a=_, b=_
      问下划线的部分应是什么,请完成。

    2. 考题二:代码如下。
      Exchg2(int *px, int *py)
      {
      int tmp=*px;
      *px=*py;
      *py=tmp;
      print(“*px=%d,*py=%d/n”,*px,*py);
      }
      main()
      {
      int a=4;
      int b=6;
      Exchg2(&a,&b);
      Print(“a=%d,b=%d/n”, a, b);
      }
      输出的结果为:
      *px=_, *py=_
      a=_, b=_
      问下划线的部分应是什么,请完成。

    3. 考题三:
      Exchg2(int &x, int &y)
      {
      int tmp=x;
      x=y;
      y=tmp;
      print(“x=%d,y=%d/n”,x,y);
      }
      main()
      {
      int a=4;
      int b=6;
      Exchg2(a,b);
      Print(“a=%d,b=%d/n”, a, b);
      }

    二. 函数参数传递方式之一:值传递

    1. 值传递的一个错误认识
    先看题一中Exchg1函数的定义:
    void Exchg1(int x, int y) //定义中的x,y变量被称为Exchg1函数的形式参数
    {
    int tmp;
    tmp=x;
    x=y;
    y=tmp;
    printf(“x=%d,y=%d/n”,x,y)
    }
    问:你认为这个函数是在做什么呀?
    答:好像是对参数x,y的值对调吧?
    请往下看,我想利用这个函数来完成对a,b两个变量值的对调,程序如下:
    void main()
    {
    int a=4,b=6;
    Exchg1 (a,b) //a,b变量为Exchg1函数的实际参数。
    / printf(“a=%d,b=%d/n”,a,b)
    }
    我问:Exchg1 ()里头的 printf(“x=%d,y=%d/n”,x,y)语句会输出什么啊?
    我再问:Exchg1 ()后的 printf(“a=%d,b=%d/n”,a,b)语句输出的是什么?
    程序输出的结果是:
    x=6 , y=4
    a=4 , b=6 //为什么不是a=6,b=4呢?

    奇怪,明明我把a,b分别代入了x,y中,并在函数里完成了两个变量值的交换,为什么a,b变量值还是没有交换(仍然是a==4,b==6,而不是a==6,b==4)?如果你也会有这个疑问,那是因为你跟本就不知实参a,b与形参x,y的关系了。
    

    2. 一个预备的常识
    为了说明这个问题,我先给出一个代码:
    int a=4;
    int x;
    x=a;
    x=x+3;
    看好了没,现在我问你:最终a值是多少,x值是多少?
    (怎么搞的,给我这个小儿科的问题。还不简单,不就是a==4 x==7嘛!)
    在这个代码中,你要明白一个东西:虽然a值赋给了x,但是a变量并不是x变量哦。我们对x任何的修改,都不会改变a变量。呵呵!虽然简单,并且一看就理所当然,不过可是一个很重要的认识喔。

    3. 理解值传递的形式
    看调用Exch1函数的代码:

    main()
    {
    int a=4,b=6;
    Exchg1(a,b) //这里调用了Exchg1函数
    printf(“a=%d,b=%d”,a,b)
    }

    Exchg1(a,b)时所完成的操作代码如下所示。
    int x=a;//←
    int y=b;//←注意这里,头两行是调用函数时的隐含操作
    int tmp;
    tmp=x;
    x=y;
    y=tmp;
    请注意在调用执行Exchg1函数的操作中我人为地加上了头两句:
    int x=a;
    int y=b;
    这是调用函数时的两个隐含动作。它确实存在,现在我只不过把它显式地写了出来而已。问题一下就清晰起来啦。(看到这里,现在你认为函数里面交换操作的是a,b变量或者只是x,y变量呢?)
    原来 ,其实函数在调用时是隐含地把实参a,b 的值分别赋值给了x,y,之后在你写的Exchg1函数体内再也没有对a,b进行任何的操作了。交换的只是x,y变量。并不是a,b。当然a,b的值没有改变啦!函数只是把a,b的值通过赋值传递给了x,y,函数里头操作的只是x,y的值并不是a,b的值。这就是所谓的参数的值传递了。
    哈哈,终于明白了,正是因为它隐含了那两个的赋值操作,才让我们产生了前述的迷惑(以为a,b已经代替了x,y,对x,y的操作就是对a,b的操作了,这是一个错误的观点啊!)。

    三. 函数参数传递方式之二:地址传递
    继续——地址传递的问题!
    看题二的代码:
    Exchg2(int *px, int *py)
    {
    int tmp=*px;
    *px=*py;
    *py=tmp;
    print(“*px=%d,*py=%d/n”,*px,*py);
    }
    main()
    {
    int a=4;
    int b=6;
    Exchg2(&a,&b);
    Print(“a=%d,b=%d/n”, a, b);
    }
    它的输出结果是:
    *px=6,*py=4
    a=6,b=4

    看函数的接口部分:Exchg2(int *px,int *py),请注意:参数px,py都是指针。
    再看调用处:Exchg2(&a, &b);
    它将a的地址(&a)代入到px,b的地址(&b)代入到py。同上面的值传递一样,函数调用时作了两个隐含的操作:将&a,&b的值赋值给了px,py。
    

    px=&a;
    py=&b;
    呵呵!我们发现,其实它与值传递并没有什么不同,只不过这里是将a,b的地址值传递给了px,py,而不是传递的a,b的内容,而(请好好地在比较比较啦)
    整个Exchg2函数调用是如下执行的:
    px=&a; //
    py=&b; //请注意这两行,它是调用Exchg2的隐含动作。
    int tmp=*px;
    *px=*py;
    *py=tmp;
    print(“*px=%d,*py=%d/n”,*px,*py);
    这样,有了头两行的隐含赋值操作。我们现在已经可以看出,指针px,py的值已经分别是a,b变量的地址值了。接下来,对*px,*py的操作当然也就是对a,b变量本身的操作了。所以函数里头的交换就是对a,b值的交换了,这就是所谓的地址传递(传递a,b的地址给了px,py),你现在明白了吗?

    四. 函数参数传递方式之三:引用传递
    看题三的代码:
    Exchg3(int &x, int &y) //注意定义处的形式参数的格式与值传递不同
    {
    int tmp=x;
    x=y;
    y=tmp;
    print(“x=%d,y=%d/n”,x,y);
    }
    main()
    {
    int a=4;
    int b=6;
    Exchg3(a,b); //注意:这里调用方式与值传递一样
    Print(“a=%d,b=%d/n”, a, b);
    }
    输出结果:
    x=6, y=4
    a=6, b=4 //这个输出结果与值传递不同。
    看到没有,与值传递相比,代码格式上只有一处是不同的,即在定义处:
    Exchg3(int &x, int &y)。
    但是我们发现a与b的值发生了对调。这说明了Exchg3(a,b)里头修改的是a,b变量,而不只是修改x,y了。
    我们先看Exchg3函数的定义处Exchg3(int &x,int &y)。参数x,y是int的变量,调用时我们可以像值传递(如: Exchg1(a,b); )一样调用函数(如: Exchg3(a,b); )。但是x,y前都有一个取地址符号&。有了这个,调用Exchg3时函数会将a,b 分别代替了x,y了,我们称x,y分别引用了a,b变量。这样函数里头操作的其实就是实参a,b本身了,也就是说函数里是可以直接修改到a,b的值了。

    最后对值传递与引用传递作一个比较:

    1. 在函数定义格式上有不同:
    值传递在定义处是:Exchg1(int x, int y);
    引用传递在这义处是:Exchg1(int &x, int &y);

    2. 调用时有相同的格式:
    值传递:Exchg1(a,b);
    引用传递:Exchg3(a,b);

    3. 功能上是不同的:
    值传递的函数里操作的不是a,b变量本身,只是将a,b值赋给了x,y函数里操作的只是x,y变量而不是a,b,显示a,b的值不会被Exchg1函数所修改。
    引用传递Exchg3(a,b)函数里是用a,b分别代替了x,y。函数里操作的是a,b。

    转自:http://blog.csdn.net/xiaosong2008/article/details/25430261

    展开全文
  • 普通函数调用:执行到调用语句时,跳到函数代码存储区,然后执行局部变量压栈、参数压栈存、保护现场(存储函数调用后继续执行的地址)等操作。执行完后,跳回调用语句处。 如:FUN_A调用FUN_B FUN_A ebp(栈堆基址...

    首先要注意的是,即使在代码中声明为内联函数,编译器也不一定接受,实现的时候不一定为内联函数。

    普通函数调用:执行到调用语句时,跳到函数代码存储区,然后执行局部参数压栈存、保护现场、变量压栈、(存储函数调用后继续执行的地址)等操作。执行完后,跳回调用语句处。

    如:FUN_A调用FUN_B

    FUN_A ebp(栈堆基址指针)入栈,esp(堆栈栈顶指针)赋值给ebp作为FUN_B基址,申请内存,压栈,FUN)B结束后,返回当前ebp恢复为FUN_A的栈顶指针esp,然后调用者A再根据ESP弹出之前的ebp,这样就恢复了调用FUN_B之前的场景。

    此处参考:http://blog.csdn.net/zsy2020314/article/details/9429707

    内联函数:直接把函数装入内存,在调用处直接执行函数代码。

    如图所示:

    1



    补充普通函数调用过程:这个感觉比较靠谱

    一直对寄存器ESP和EBP的概念总是有些混淆,查看定义ESP是栈顶指针,EBP是存取堆栈指针。还是不能很透彻理解。之后借于一段汇编代码,总算是对两者有个比较清晰的理解。
    下面是按调用约定__stdcall 调用函数test(int p1,int p2)的汇编代码
    ;假设执行函数前堆栈指针ESP为NN
    push   p2    ;参数2入栈, ESP -= 4h , ESP = NN - 4h
    push   p1    ;参数1入栈, ESP -= 4h , ESP = NN - 8h
    call test    ;压入返回地址 ESP -= 4h, ESP = NN - 0Ch     (EIP?)
    ;//进入函数内
    {
    push   ebp                        ;保护先前EBP指针, EBP入栈, ESP-=4h, ESP = NN - 10h
    mov    ebp, esp                   ;设置EBP指针指向栈顶 NN-10h
    mov    eax, dword ptr  [ebp+0ch]  ;ebp+0ch为NN-4h,即参数2的位置
    mov    ebx, dword ptr  [ebp+08h]  ;ebp+08h为NN-8h,即参数1的位置
    sub    esp, 8                     ;局部变量所占空间ESP-=8, ESP = NN-18h
    ...
    add    esp, 8                     ;释放局部变量, ESP+=8, ESP = NN-10h
    pop    ebp                        ;出栈,恢复EBP, ESP+=4, ESP = NN-0Ch
    ret    8                          ;ret返回,弹出返回地址,ESP+=4, ESP=NN-08h, 后面加操作数8为平衡堆栈,ESP+=8,ESP=NN, 恢复进入函数前的堆栈.
    }
    看完汇编后,再看EBP和ESP的定义,哦,豁然开朗,
    原来ESP就是一直指向栈顶的指针,而EBP只是存取某时刻的栈顶指针,以方便对栈的操作,如获取函数参数、局部变量等。



    展开全文
  • 理解函数调用

    2018-05-17 10:00:09
    函数调用过程中,需要切换堆栈, 先介绍几个常用的寄存器 ESP 堆栈指针寄存器,这个寄存器指向当前堆栈的栈顶 EBP 堆栈基地址寄存器,这个寄存器指向堆栈的栈底 EIP 指令寄存器,保存了下一条指令的地址 有了...

    在函数调用过程中,需要切换堆栈,
    先介绍几个常用的寄存器
    ESP 堆栈指针寄存器,这个寄存器指向当前堆栈的栈顶
    EBP 堆栈基地址寄存器,这个寄存器指向堆栈的栈底
    EIP 指令寄存器,保存了下一条指令的地址

    有了上面的铺垫,开始接受当函数调用时候指向CALL以及返回RET时候堆栈的状态,
    当执行CALL的时候cs:eip以及指向了函数调用后的下一条指令,此时需要将cs:eip压入堆栈,以便调用完继续执行。
    这里写图片描述
    接下来,就是需要保存当前的堆栈,所以把当前的EBP也压入堆栈
    这里写图片描述
    这样原理的堆栈基地址就保存了,此时把ESP 赋值给EBP,此时
    这里写图片描述
    这样新的函数栈就建立了,此时的esp和ebp相等,等于说是一个空栈
    那么接下来函数内部变量就可以使用这个栈了,
    这里写图片描述
    这样堆栈就实现了函数的调用的切换
    那如果函数调用结束后呢,此时就需要还原,
    退出逻辑正好相反,此时需要把EBP的内容再复制给ESP,然后再弹出(pop)EBP,这样堆栈机恢复了,然后就可以RET了,下面接直接弹出cs:eip继续执行。

    展开全文
  • 函数调用(递归函数调用)底层机制

    千次阅读 2013-07-01 16:32:47
    函数调用的底层实现  通常,当在一个函数的运行期间调用另一个函数时,在运行被调用函数之前,系统需要先完成3件事:  (1)将所有的实在参数,返回地址等信息传递给被调用函数保存  (2)为被调用函数的局部...

    函数调用的底层实现

      通常,当在一个函数的运行期间调用另一个函数时,在运行被调用函数之前,系统需要先完成3件事:
      (1)将所有的实在参数,返回地址等信息传递给被调用函数保存
      (2)为被调用函数的局部变量分配存储区
      (3)将控制转移到被调用函数的入口
      被调用函数返回调用函数之前,系统也应该完成3件工作
      (1)保存被调用函数的计算结果
      (2)释放被调函数的数据区
      (3)依照被调函数保存的返回地址将控制权转移到调用函数
       
       



      补充:Hanoi问题描述如下:
       
       

      转载自:数据结构(C语言版) (55-58页) 感觉写的较详细所以就粘贴过来
    展开全文
  • 我想,你只要看了C语言上关于传值函数调用的测试题,一切都会了然于胸:1. 考题一:程序代码如下:void Exchg1(int x, int y) {int tmp;tmp=x;x=y;y=tmp;printf(“x=%d,y=%d/n”,x,y)}void main(){int a=4,b=6;...
  • 函数调用

    2012-12-13 15:36:22
    当一个函数在运行时,如需要调用另外一个函数或者该函数本身,则需要进行以下三个操作 1.将运行函数的所有参数、值以及返回地址传递给被调函数保存 ...3.按照被调函数调用时保存的返回地址将控制转移到调用函数
  • 通过函数的入口地址调用函数

    千次阅读 2018-06-12 09:30:27
    例程:int i; //定义一个测试变量void test() //定义一个函数{i = 6; //给测试变量赋初值}int main(){int addr; //定义一个保存地址的变量addr = (int)... //根据函数入口地址调用test函数//((void(*)(void))addr)(...
  • 0:000> dds poi(00405798+2) l1 0051b710 7e42974e user32!GetCursorPos 命令的原理是这样的。以下面这条指令为例: ...CALL指令调用地址0041a908处通常是一条跳转指令: 00405798 ff2510b75100 jmp dword
  • 函数地址调用函数 C++ MFC

    千次阅读 2016-09-12 05:04:24
    所以想通过函数地址调用函数,并且能正常的传递参数.但调用过程和被调函数是不在一个类的,调用起来又是各种麻烦 这几天我翻烂了百度, 都没找到方法,什么函数回调啊,通过函数地址调用函数啊,还有什么接口之类的
  • 进程的地址空间与函数调用过程

    千次阅读 2016-04-10 14:35:16
    要知道C语言的函数调用过程,首先要明白C语言中的各部分代码都出现在什么段。 首先来看一串代码,代码中的各个部分都有自己对应的段,换句话说每个段都存有C语言中的各个部分代码,而这所有的代码组合起来才成为一个...
  • url中输入调用函数函数调用ajax请求 转载于:https://www.cnblogs.com/nizuimeiabc1/p/8882398.html
  • 深入理解函数调用跳转地址

    千次阅读 2019-02-20 22:57:20
    函数调用之后会返回,那返回地址如何得到? 以add函数为例接着进行分析: 在add函数设置断点,执行F11后,查看变化的寄存器有两个: EIP:保存call跳转到的指令的地址0x804841d; ESP: 该值或者是返回地址...
  • 函数调用过程

    2018-03-22 23:20:28
    ax(accumulator): 可用于存放函数返回值bp(base pointer): 用于存放执行中的函数对应的栈帧的栈底地址sp(stack poinger): 用于存放执行中...这里以一个简单的C语言代码为例,来分析函数调用过程 代码: 1 #include
  • C语言函数调用栈(一)

    万次阅读 多人点赞 2018-07-19 22:16:25
    编译器使用堆栈传递函数参数、保存返回地址、临时保存寄存器原有值(即函数调用的上下文)以备恢复以及存储本地局部变量。 不同处理器和编译器的堆栈布局、函数调用方法都可能不同,但堆栈的基本概念是一...
  • 就是我们为什么要把一个函数地址作为参数传递给另一个参数。要知道在C语言中,一个函数内部是可以直接调用其他函数的,既然可以直接调用,为什么还要用这么麻烦的办法去把函数当做参数来传递呢。下面我举个例子。 ...
  • C++函数调用过程和内置函数详解

    千次阅读 2018-03-12 10:15:01
    上图表示函数调用过程:①程序先执行函数调用之前的... 这样就要求在转到被调用函数之前,要记下当时执行的指令的地址,还要“保护现场”(记下当时有关的信息),方便在函数调用之后继续执行。在函数调用之后,流程
  • 首先函数名、函数指针都表示代码段的起始地址。 1)调用函数的时候必须指定函数名,可是当有时候不确定具体调用哪个函数,当某些事件发生后才确定,所以事先就定义一个函数指针(比如回调函数) 2)函数调用...
  • 函数调用原理总结

    千次阅读 2018-11-22 15:58:00
    函数调用是个很有意思的东西,之前一直都很好奇:函数调用结束后是怎么知道返回什么地方?函数中的各个参数又是从哪来的呢?调用结束后又怎么将相关数据返回到调用方的?基于这些疑问,参阅很多资料,现在把自己所...
  • 类的函数调用

    千次阅读 2018-06-04 10:42:04
    2.涉及多态性时,采用虚函数和动态绑定,函数调用在运行时绑定,而非在编译时绑定,此时不再单独根据指针(引用)类型来判断调用的函数,而是根据对象中虚指针指向的虚表中的函数地址来确定调用的函数。father.h ...
  • 调用函数时,有三种向函数传递参数的方式: 调用类型 描述 传值调用 该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数对实际参数没有影响。 指针调用 该方法把参数的地址...
  • Linux函数调用与栈

    千次阅读 2017-06-05 17:31:36
    栈与函数调用惯例(又称调用约定)— 基础篇 记得一年半前参加百度的校招面试时,被问到函数调用惯例的问题。当时只是懂个大概,比如常见函数调用约定类型及对应的参数入栈顺序等。最近看书过程中,重新回顾了这些...
  • 同上面的值传递一样,函数调用时作了两个隐含的操作:将&a,&b的值赋值给了px,py。 px=&a; py=&b; 呵呵!我们发现,其实它与值传递并没有什么不同,只不过这里是将a,b的地址值传递给了px,py,而不是传递的a,b...
  • 首先函数名、函数指针都表示代码段的起始地址。 1)调用函数的时候必须指定函数名,可是当有时候不确定具体调用哪个函数,当某些事件发生后才确定,所以事先就定义一个函数指针(比如回调函数) 2)函数调用有...
  • 在构造函数调用函数函数的入口地址是在编译时静态确定的,并未实现虚调用。但是为什么在构造函数调用函数,实际上没有发生动态联编呢?1. 不要在构造函数调用函数的原因第一个原因,在概念上,构造...
  • 关于将函数的地址作为参数传递给另一个函数调用

    千次阅读 多人点赞 2015-10-26 22:38:25
    就是我们为什么要把一个函数地址作为参数传递给另一个参数。要知道在C语言中,一个函数内部是可以直接调用其他函数的,既然可以直接调用,为什么还要用这么麻烦的办法去把函数当做参数来传递呢。下面我举个例子。 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 26,929
精华内容 10,771
关键字:

地址函数调用