函数指针的解引用是如何发生的?
为什么以及如何取消引用函数指针只是“什么都不做”?
这就是我所说的:
#include<stdio.h> void hello() { printf("hello"); } int main(void) { (*****hello)(); }
从这里发表评论:
函数指针解引用就好了,但是得到的函数指示符会立即转换回函数指针
从这里的答案:
解除引用(以您认为的方式)函数的指针意味着:访问一个CODE内存,因为它将是一个DATA内存。
函数指针不能被这样解除引用。 相反,它被称为。
我会用“呼叫”并排使用名称“取消引用”。 没关系。
无论如何:C被devise为使得函数名称标识符以及variables保持函数的指针与代码存储器的地址相同。 而且它允许通过在标识符或variables上使用call()语法跳转到该内存。
函数指针的解引用究竟如何工作?
这不是一个正确的问题。 至less对于C来说,正确的问题是
右值上下文中的函数值会发生什么?
(右值上下文是任何地方出现名称或其他引用的地方,它应该被用作值,而不是位置 – 除了在赋值的左边,基本上任何地方都是这样的。名字本身来自右边一个任务。)
好的,右值上下文中的函数值会发生什么变化? 它被立即隐含地转换为指向原始函数值的指针。 如果用*
取消引用该指针,则会再次返回相同的函数值,并立即隐式转换为指针。 而且你可以随心所欲的多次这样做。
两个类似的实验,你可以尝试:
-
如果您在左值上下文中取消引用函数指针,则会发生什么情况 – 分配的左侧。 (如果你记住函数是不可变的,那么答案就是你所期望的。
-
一个数组值也被转换为左值上下文中的指针,但它被转换为指向元素types的指针,而不是指向数组的指针。 解引用它会给你一个元素,而不是一个数组,而你显示的疯狂不会发生。
希望这可以帮助。
PS至于为什么一个函数值被隐式地转换为一个指针,答案就是对于那些使用函数指针的人来说,这是一个很好的方便,不需要在任何地方使用&
。 还有一个双重便利:函数指针在调用位置被自动转换为函数值,所以你不必写*
来通过函数指针调用。
PPS与C函数不同,C ++函数可能会被重载,而且我没有资格评论C ++中的语义是如何工作的。
C ++ 03§4.3/ 1:
函数typesT的左值可以转换为types“指向T的指针”的右值。结果是指向函数的指针。
如果您尝试对一个函数引用(如一元*
运算符)进行无效操作,则语言尝试的第一件事是标准转换。 就像在将一个int
添加到一个float
。 在函数引用上使用*
会导致语言取其指针,在您的示例中,它是方形1。
另一个适用的情况是分配一个函数指针。
void f() { void (*recurse)() = f; // "f" is a reference; implicitly convert to ptr. recurse(); // call operator is defined for pointers }
请注意,这不能用另一种方式。
void f() { void (&recurse)() = &f; // "&f" is a pointer; ERROR can't convert to ref. recurse(); // OK - call operator is *separately* defined for references }
函数引用variables是很好的,因为它们(理论上我从来没有testing过)向编译器提示:如果在一个封闭的范围内进行了初始化,那么间接分支可能是不必要的。
在C99中,取消引用函数指针会产生一个函数指示符。 §6.3.2.1/ 4:
函数指示符是具有函数types的expression式。 除了sizeof运算符的操作数或者一元&运算符以外,函数指示符的types为函数返回types,被转换为具有指向函数返回types的指针的expression式。
这更像诺曼的答案,但值得注意的是C99没有右值的概念。
把自己置于编译器编写者的脚下。 函数指针具有明确定义的含义,它是一个指向代表机器代码的字节块的指针。
当程序员解引用函数指针时,你做什么? 你把机器码的第一个(或8个)字节重新解释为一个指针? 赔率是20亿到一个,这是行不通的。 你是否申报UB? 已经有很多这样的事情了。 或者你忽略了这个尝试? 你知道答案。
函数指针的解引用究竟如何工作?
两个步骤。 第一步是编译时,第二步是在运行时。
在第一步中,编译器发现它有一个指针和一个上下文,在这个上下文中,这个指针被解引用(比如(*pFoo)()
),所以它为这种情况生成代码,这个代码将在第二步中使用。
在步骤2中,在运行时执行代码。 指针包含一些字节,指示下一个应该执行哪个函数。 这些字节以某种方式加载到CPU中。 一个常见的情况是具有明确的CALL [register]
指令的CPU。 在这样的系统中,函数指针可以简单地作为内存中函数的地址,并且去扩展代码只不过是将该地址加载到寄存器中,然后加载CALL [register]
指令。