C中的功能指针存储器说明

时间:2013-08-16 23:42:35

标签: c stack function-pointers

#include <stdio.h>
#include <stdlib.h>

int (*fptr1)(int);

int square(int num){
  return num*num;
}

void main(){
  fptr1 = &square;
  printf("%d\n",fptr1(5));
}

当我们调用函数指针时,有人可以简要解释堆栈中发生的事情吗?直接在main()中调用函数和通过C语言中的函数指针通过物理内存和进程调用它有什么区别?

当我们用函数指针调用函数时,我试图理解内存中发生了什么,但这对我来说还不够。

  1. 当我们通过指针调用函数时,指针是否在代码空间中具有此函数的位置?
  2. 当被调用函数运行时,它与main()中通常称为函数的相同?
  3. 当代码在流水线分支预测处理器中运行时,直接调用函数或使用函数指针有什么区别?

4 个答案:

答案 0 :(得分:10)

回答这个问题的最好方法是查看反汇编(稍加修改的样本):

fptr1 = &square;
int result1 = fptr1(5);
int result2 = square(5);

此x64 asm中的结果:

    fptr1 = &square;
000000013FA31A61  lea         rax,[square (013FA31037h)]  
000000013FA31A68  mov         qword ptr [fptr1 (013FA40290h)],rax  
    int result1 = fptr1(5);
000000013FA31A6F  mov         ecx,5  
000000013FA31A74  call        qword ptr [fptr1 (013FA40290h)]  
000000013FA31A7A  mov         dword ptr [result1],eax  
    int result2 = square(5);
000000013FA31A7E  mov         ecx,5  
000000013FA31A83  call        square (013FA31037h)  
000000013FA31A88  mov         dword ptr [result2],eax  

正如您所看到的,直接调用函数和通过指针调用程序集几乎相同。在这两种情况下,CPU都需要访问代码所在的位置并调用它。直接调用的好处是不必取消引用指针(因为偏移将被烘焙到程序集中)。

  1. 是的,你可以在函数指针的赋值中看到,那个 它存储'square'函数的代码地址。
  2. 从堆栈中 设置/拆除:是的。从绩效的角度来看,有一个 如上所述,略有不同。
  3. 没有分支,所以这里没有区别。
  4. 编辑:如果我们要将分支插入到上面的示例中,那么用尽有趣的场景就不会花费很长时间,所以我将在这里解决它们:

    如果我们在加载(或赋值)函数指针之前有一个分支,例如(在伪程序集中):

    branch zero foobar
    lea square
    call ptr
    

    然后我们可以有所不同。假设管道选择加载并开始处理foobar处的指令,然后当它意识到我们实际上不会采用该分支时,它必须停止以加载函数指针和解除引用它。如果我们只是叫一个知道地址,那么就不会有失速。

    案例二:

    lea square
    branch zero foobar
    call ptr
    

    在这种情况下,直接调用与通过函数指针之间没有任何区别,因为我们需要的一切都已知道处理器是否开始执行错误的路径,然后重置以开始执行调用。

    第三种情况是分支跟随调用,从管道的角度来看,这显然不是很有趣,因为我们已经执行了子程序。

    所以要完全重新回答问题 3 ,我会说是的,有区别。但真正的问题是编译器/优化器是否足够智能以在函数指针赋值后移动分支,因此它属于案例2而不是案例1。

答案 1 :(得分:5)

  1. 指向函数的指针包含程序文本段中函数开头的地址。
  2. 一旦被调用,该函数的运行方式是相同的,无论是直接调用还是通过指向函数的指针。
  3. 我不确定。通常,不会有太大差异;如果有的话,指向函数的指针不会经常更改(例如,因为你动态加载了一个共享库,所以你必须使用指向函数的指针来调用函数)。
  4.   

    直接在main()中调用函数并通过函数指针调用函数有什么区别?

    唯一的区别可能是从内存中获取函数指针的额外内存引用。

答案 2 :(得分:3)

  1. 绝对
  2. 没有区别
  3. 通过函数指针调用函数只是一种方便;并且可以将函数作为参数传递给另一个函数。例如,参见qsort()

答案 3 :(得分:2)

通过名称调用函数和通过函数指针调用函数没有区别。函数指针的目的是让你可以调用在其他地方指定的函数。例如,qsort()函数将函数指针作为一个参数,它调用它来确定如何对数组的元素进行排序,而不是只有一个比较方法。