C中的函数指针和内存地址
function pointer and memory address in C
在下面的程序中,&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;
}
输出是:
arg = 0
arg = 0
arg = 0
&foo = 55eeef54c720
foo = 55eeef54c720
*foo = 55eeef54c720
有谁能解释一下吗?
谢谢。
在 C 标准的术语中,任何具有函数类型的表达式都是 函数指示符。因此,当用作表达式时,函数的名称是函数指示符。
除了获取其地址外,您无法对函数指示符执行任何操作。您不能将数字添加到功能指示符,也不能将其与另一个功能指示符进行比较,等等。当然,你可以调用一个函数,但这实际上是通过地址来完成的,而不是通过指示符,我稍后会解释。由于除了获取函数的地址之外,您无法对函数执行任何操作,因此 C 会自动为您完成此操作。根据 C 2018 6.3.2.1 4:
Except when it is the operand of the sizeof
operator, or the unary &
operator, a function designator with type "function returning type" is converted to an expression that has type "pointer to function returning type".
结果是:
- 在
&foo
中,&
取foo
的地址,所以&foo
是foo
的地址。
- 在
foo
中,函数标志符自动转换为函数地址,所以foo
是&foo
。
- 在
*foo
中,函数标识符自动转换为函数地址。然后 *
运算符将其转换回函数,从而生成函数指示符。然后自动转换再次发生,函数指示符被转换回函数的地址,所以*foo
的结果是&foo
。
当您调用函数时,使用 function ( argument-list... )
表示法,function
实际上必须是指向函数的指针。因此,您可以使用 (&foo)(arguments...)
调用 foo
。自动转换简化了语法,所以你可以写成 foo(arguments...)
,但调用表达式实际上需要函数的地址,而不是函数指示符。
顺便提一句:
size_t
值的正确 printf
说明符是 %zx
,而不是 %lx
。
- 如果您包含
<stdint.h>
,它定义了一个用于将指针转换为整数的类型,uintptr_t
。这最好是将指针转换为 size_t
.
函数 foo
可隐式转换为指向函数 foo
的指针。
应用于函数的一元 &
产生指向函数的指针,就像它应用于变量时产生变量的地址一样。
应用于函数指针的一元*
产生指向的函数,就像它应用于指向变量的普通指针时产生指向的变量一样。
所以在这里,foo
与 &foo
相同,后者与 *foo
相同。
因此 *foo
与 *(&foo)
相同,后者与 foo
相同。
在下面的程序中,&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;
}
输出是:
arg = 0
arg = 0
arg = 0
&foo = 55eeef54c720
foo = 55eeef54c720
*foo = 55eeef54c720
有谁能解释一下吗? 谢谢。
在 C 标准的术语中,任何具有函数类型的表达式都是 函数指示符。因此,当用作表达式时,函数的名称是函数指示符。
除了获取其地址外,您无法对函数指示符执行任何操作。您不能将数字添加到功能指示符,也不能将其与另一个功能指示符进行比较,等等。当然,你可以调用一个函数,但这实际上是通过地址来完成的,而不是通过指示符,我稍后会解释。由于除了获取函数的地址之外,您无法对函数执行任何操作,因此 C 会自动为您完成此操作。根据 C 2018 6.3.2.1 4:
Except when it is the operand of the
sizeof
operator, or the unary&
operator, a function designator with type "function returning type" is converted to an expression that has type "pointer to function returning type".
结果是:
- 在
&foo
中,&
取foo
的地址,所以&foo
是foo
的地址。 - 在
foo
中,函数标志符自动转换为函数地址,所以foo
是&foo
。 - 在
*foo
中,函数标识符自动转换为函数地址。然后*
运算符将其转换回函数,从而生成函数指示符。然后自动转换再次发生,函数指示符被转换回函数的地址,所以*foo
的结果是&foo
。
当您调用函数时,使用 function ( argument-list... )
表示法,function
实际上必须是指向函数的指针。因此,您可以使用 (&foo)(arguments...)
调用 foo
。自动转换简化了语法,所以你可以写成 foo(arguments...)
,但调用表达式实际上需要函数的地址,而不是函数指示符。
顺便提一句:
size_t
值的正确printf
说明符是%zx
,而不是%lx
。- 如果您包含
<stdint.h>
,它定义了一个用于将指针转换为整数的类型,uintptr_t
。这最好是将指针转换为size_t
.
函数 foo
可隐式转换为指向函数 foo
的指针。
应用于函数的一元 &
产生指向函数的指针,就像它应用于变量时产生变量的地址一样。
应用于函数指针的一元*
产生指向的函数,就像它应用于指向变量的普通指针时产生指向的变量一样。
所以在这里,foo
与 &foo
相同,后者与 *foo
相同。
因此 *foo
与 *(&foo)
相同,后者与 foo
相同。