C语言中的函数指针和内存地址

C语言中的函数指针和内存地址,c,linux,gcc,function-pointers,C,Linux,Gcc,Function Pointers,在以下程序中,&foo、*foo和foo指向相同的内存地址: #include <stdio.h> int foo(int arg) { printf("arg = %d\n", arg); return arg; } int main() { foo(0); // ok (*foo)(0); // ok (&foo)(0); // ok printf("&foo = %lx\n", (size_t)(&f

在以下程序中,
&foo
*foo
foo
指向相同的内存地址:

#include <stdio.h>

int foo(int arg)
{
    printf("arg = %d\n", arg);
    return arg;
}

int main()
{
    foo(0); // ok
    (*foo)(0); // ok
    (&foo)(0); // ok

    printf("&foo = %lx\n", (size_t)(&foo));
    printf("foo = %lx\n", (size_t)(foo));
    printf("*foo = %lx\n", (size_t)(*foo));

    return 0;
}
有人能解释吗?
谢谢。

在C标准术语中,任何具有函数类型的表达式都是函数指示符。因此,当用作表达式时,函数的名称是函数指示符

对于函数指示符,除了获取其地址外,您无法执行任何操作。不能将数字添加到函数指示符,也不能将其与另一个函数指示符进行比较,依此类推。当然,您可以调用函数,但这实际上是通过地址完成的,而不是通过指示符完成的,我稍后会解释。由于除了获取函数的地址外,您无法对函数执行任何操作,因此C会自动为您执行此操作。根据C 2018 6.3.2.1 4:

除非它是
sizeof
运算符或一元
&
运算符的操作数,否则类型为“函数返回类型”的函数指示符将转换为类型为“指向函数返回类型的指针”的表达式

其结果是:

  • &foo
    中,
    &
    foo
    的地址,因此
    &foo
    foo
    的地址
  • foo
    中,函数指示符自动转换为函数的地址,因此
    foo
    &foo
  • *foo
    中,函数指示符自动转换为函数的地址。然后,
    *
    运算符将其转换回函数,从而生成函数指示符。然后自动转换再次发生,函数指示符被转换回函数的地址,因此
    *foo
    的结果是
    &foo
调用函数时,使用
函数(参数列表…
表示法,
函数实际上必须是指向函数的指针。因此,您可以使用
(&foo)(参数…
)调用
foo
。自动转换简化了语法,因此您可以编写
foo(arguments…
),但调用表达式实际上需要函数的地址,而不是函数指示符

顺便说一下:

  • size\u t
    值的合适
    printf
    说明符是
    %zx
    ,而不是
    %lx
  • 如果包含
    ,它将定义一种用于将指针转换为整数的类型,
    uintptpr\t
    。最好将指针转换为
    size\t

函数
foo
可隐式转换为指向函数
foo
的指针

应用于函数的一元
&
生成指向函数的指针,就像应用于变量时生成变量的地址一样

应用于函数指针的一元
*
生成指向函数,就像应用于指向变量的普通指针时生成指向变量一样

所以这里,
foo
&foo
相同,后者与
*foo
相同

因此
*foo
*(&foo)
相同,后者与
foo

函数名(sysmbol)实际上是它自身的内存地址。当链接可执行文件时,可以用内存地址更改函数名。像
call foo
这样的CPU指令是跳转到
foo
函数CPU机构(代码)所在的RAM块。
arg = 0
arg = 0
arg = 0
&foo = 55eeef54c720
foo = 55eeef54c720
*foo = 55eeef54c720