繁体   English   中英

在C / C ++中通过指针调用函数时,编译器如何正确处理ABI?

[英]How compilers handle the ABI correctly when calling a function by pointer in C/C++?

编译器如何知道函数指针指向的函数的ABI?

在计算机软件中,应用程序二进制接口(ABI)是两个程序模块之间的接口。

因为在C / C ++中,函数指针只指定API,不知道它实际使用的是哪种ABI,这对编译器来说不是一个问题,特别是当他们无法静态地解决这个问题时?

这是否意味着使用这种指针的程序员需要手动指定调用约定?

如果是这样,怎么办? 任何人都可以给我一些关于这个编译器文档的链接吗?

如果函数使用的调用约定不同于您所使用的平台的默认约定,那么是的,您需要手动指定调用约定。 大多数平台尝试使用单个ABI,但具有单个或可预测的一组调用约定,因此任何编译器都知道如何调用任何函数。

虽然这超出了C ++的范围,但是如果您可以或者需要指定调用约定,则可以使用您正在使用的编译器的非标准扩展来完成。

gcc和我怀疑所有主要的编译器通过使一个函数(和指向这些函数的指针)具有不同类型的不同调用约定来解决这个问题。 让我们来看看:

__attribute__ ((noinline)) auto sum1(int a, int b) { return a + b; }
__attribute__ ((noinline)) auto sum2(int a, int b) __attribute__((fastcall));
auto sum2(int a, int b) { return a + b; }

auto test1(int a, int b) { return sum1(a, b); }
auto test2(int a, int b) { return sum2(a, b); }
sum1(int, int):
  mov eax, DWORD PTR [esp+8]
  add eax, DWORD PTR [esp+4]
  ret
sum2(int, int):
  lea eax, [ecx+edx]
  ret
test1(int, int):
  jmp sum1(int, int)
test2(int, int):
  mov edx, DWORD PTR [esp+8]
  mov ecx, DWORD PTR [esp+4]
  jmp sum2(int, int)

我们可以清楚地看到上面的两个函数以不同的方式被调用。

当我们在混合中抛出指针时会发生什么:

__attribute__ ((noinline)) auto call1(int a, int b, auto (*f)(int, int) -> int)
{
    return f(a, b);
}

__attribute__ ((noinline))
auto call2(int a, int b, auto (__attribute__((fastcall)) *f )(int, int) -> int  )
{
    return f(a, b);
}


auto test(int a, int b)
{
    call1(a, b, sum1);
    // call2(a, b, sum1); // compiler error

    // call1(a, b, sum2); // compiler error
    call2(a, b, sum2);
}

编译器不允许将函数指针转换为指向不同调用约定的函数的指针。

错误:从int (__attribute__((fastcall)) *)(int, int)int (*)(int, int)无效转换[-fpermissive]

  call1(a, b, sum2); ^~~~ 

godbolt上玩它

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM