传递函数指针给静态函数,这是可能的/安全的/明智的吗?

10

如果我想通过传递一个函数指针从我的文件中仅公开一个函数,那么将该函数声明为 static 是否安全?编译器是否允许进行任何操作来使我的函数指针无效或使其仅在该文件上下文之外失去意义,因为该函数被声明为特定于该文件?

这不是我的代码,而是我想表达的(愚蠢)示例:

void    static   cool_function(void);
void    extern (*cool_function_ptr)(void); // Actually, I’m not sure of where the `extern` goes in a function-
                                           // pointer declaration. Damn you, confusing function-pointer syntax!

考虑到这段代码(或者其近似的语法正确版本),从另一个文件中访问cool_function_ptr是否是非法的?


1
所有的回答都非常好。有时候 Stack Overflow 超出了我的预期!:D - ELLIOTTCABLE
你可以随时这样做。当你在SO上提问时,会惊讶于不同维度的答案。 - Shamim Hafiz - MSFT
我通常会得到很多不相关的答案,以及一些没有经过深思熟虑的回复,只是为了成为新问题中最先回答的人之一,从而几乎可以保证获得点赞(甚至接受)。每个答案都相关,更不用说每个答案都是有用的,这种情况很少见! - ELLIOTTCABLE
5个回答

11

这样做是完全安全的,而且通常也很有用。对于static变量和数据指针也是如此。如果编译器想要进行任何花哨的优化(比如非标准调用约定、内联、重构等等),可能会干扰通过不同的翻译单元从函数指针调用函数的能力,那么这就是编译器的责任,要么确定函数的地址从未被使用,要么生成多个代码版本中的一个,可以安全地使用标准调用约定从外部调用。


这正是我想知道的。你有相关规范的几个链接吗?虽然我相信你的话,但我很好奇,总想学更多。 - ELLIOTTCABLE
1
规范中相关部分仅涉及有关函数指针的文字,以及缺乏任何禁止使用具有静态链接的函数指针的声明。 - R.. GitHub STOP HELPING ICE
3
这个常常被使用,例如在填写结构体时,这些结构体包含了函数指针,代表着由多个模块实现的共同API(例如:内核中的文件系统)。 - Conrad Meyer

3
当然。函数指针只是一个地址,没有什么神奇的东西。
以下是一个例子:
$ cat Makefile
.PHONY: all
all:    main
        ./main
main:   main.c other.c 
        gcc -o main main.c other.c 
$ cat main.c
#include <stdio.h>

static void goodnight(){
  printf("Goonight, gracie!\n");
  return;
}

int
main(char * argv[], int argc){
  other(goodnight);
  return 0;
}
$ cat other.c
#include <stdio.h>

void other(void(*fp)()){
  fp();
  return ;
}
$ make
gcc -o main main.c other.c 
./main
Goonight, gracie!
$ 

难点在于正确声明一个接受返回void指针函数指针的函数。


请看我与Aredridel的讨论;我不担心获取地址,我担心优化编译器是否会破坏该地址。明白我的意思吗? - ELLIOTTCABLE
是的,答案是“不”。实际上,这可能会阻碍其他优化,因为它无法(例如)识别可内联的代码,因为编译器不知道哪些代码将在那里。 - Charlie Martin

1
唯一可能会咬你的是如果函数被内联 -- 并且为指针取地址应该可以防止这种情况发生。
这样做有点让调用者不知道完整的签名,所以你在玩火并希望事情能够顺利进行。
而且它是 "extern void" 和 "static void" -- 存储类,然后返回类型。

这就是我担心的事情。不是编译器是否允许,而是优化后是否合法将我的指针清除。你有相关规范链接吗?(此外,存储类和类型可互换吗?) - ELLIOTTCABLE
另外,“keeps the caller from knowing the full signature”是什么意思?函数签名仍然会在头文件中,只是API消费者被强制通过指针而不是直接调用函数,是吗? - ELLIOTTCABLE
这将取决于调用者如何获取指针。如果您在头文件中声明它为静态变量,那么调用者将获得自己的副本,并且编译器会抱怨在该单元中未实现它。签名将匹配,但代价是让事情变得混乱。不过您可以将其 typedef 为该类型的指针。 - aredridel
优化不应该像那样破坏有效的代码。获取函数地址是合法的,除非你明确地说“这个函数真的很特别,并且__implspecific__ inline __foo__”。 - aredridel
谢谢提供信息。我很好奇是谁投了你的反对票,因为你在这篇帖子上又回到了0,总共有19个赞。o_O - ELLIOTTCABLE

1

指向静态函数的指针并没有太大区别。

然而,将指针传递给其他地方可能会比直接调用略微低效。我不是很清楚你为什么这样做,但对我来说这不太合理。


1

肯定可以,可能非常合理


该语言允许您将指向模块外部的静态函数的指针传递出去。

因此,编译器在编译模块内调用时可能会执行的任何技巧都必须与您所做的操作兼容。

总的来说,相对于使函数全局化,我认为这实际上是一个很好的代码模式,因为您还可以封装函数,尽管对于没有外部引用的简单静态函数,人们可以提出相反的论点。

总之,我认为重要的是您的对象和应用程序的设计,而不是是否全局引用静态函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接