在一次采访中,他们问我在编写嵌入式系统的代码时,使用函数指针是否有益(在速度方面)?我不知道嵌入式系统,所以无法回答这个问题。只是一个阴天或模糊的答案。 那么真正的好处是什么?速度,可读性,维护,成本?
答案 0 :(得分:21)
我想也许Viren Shakya的回答错过了面试官试图引出的观点。在某些结构中,使用函数指针可以加速执行。例如,如果你有一个索引,使用它来索引一个函数指针数组可能比一个大开关更快。
但是,如果您要将静态函数调用与通过指针调用进行比较,那么Viren正确地指出还有一个额外的操作来加载指针变量。但没有人合理地尝试以这种方式使用函数指针(只是作为直接调用的替代方法)。
通过指针调用函数不是直接调用的替代方法。所以,“优势”的问题是有缺陷的;它们用于不同的环境,通常用于简化其他代码逻辑和控制流程,而不仅仅是避免静态函数调用。它们的用处在于,要调用的函数的确定是在运行时由代码动态执行,而不是由链接器静态执行。从这个意义上讲,它们在嵌入式系统中当然是有用的,但不是出于与嵌入式系统有关的任何原因。
答案 1 :(得分:17)
有很多用途。
嵌入式系统中函数指针的最重要用途是创建向量表。许多MCU架构使用位于NVM中的地址表,其中每个地址指向ISR(中断服务例程)。这样的向量表可以用C语言编写为函数指针数组。
函数指针对回调函数也很有用。作为现实世界的一个例子,前几天我正在为片上实时时钟编写驱动程序。芯片上只有一个时钟,但我需要很多定时器。这是通过为每个软件定时器保存一个计数器来解决的,该计数器由实时时钟中断增加。数据类型如下所示:
typedef struct
{
uint16_t counter;
void (*callback)(void);
} Timer_t;
当硬件定时器与软件定时器相同时,通过与计数器一起存储的函数指针调用用户指定的回调函数。像上面这样的东西是嵌入式系统中非常常见的结构。
在创建引导加载程序等时,函数指针也很有用,您将在运行时将代码写入NVM,然后调用它。您可以通过函数指针执行此操作,但不能通过链接函数执行此操作,因为代码实际上并不在链接时。
如上所述,函数指针当然对许多优化很有用,比如优化掉每个“case”是相邻数字的switch语句。
答案 2 :(得分:7)
另一件需要考虑的事情是,这个问题将是展示您在开发过程中如何制定设计决策的好机会。我能想象给出的一个回应是转过身来考虑你的实施方案。从Casey和Lundin的答案中得到一个页面,我发现回调函数非常有用,可以将模块彼此隔离并使代码更改更容易,因为我的代码处于永久原型设计阶段,而且事情变化很快且经常发生。我目前关注的是易于开发,而不是速度。
在我的情况下,我的代码通常涉及具有多个模块,这些模块需要相互发信号以同步操作顺序。以前我把它作为一整套标志和数据结构实现了extern链接。通过这种实现,两个问题通常会占用我的时间:
使用回调函数,问题就会消失,因为函数成为信号机制,并且您可以利用这些优势:
目前,即使使用了所有额外的函数调用,我的设备仍然能够充分发挥性能。当性能开始成为一个更大的问题时,我会考虑我的替代方案。
回到面试问题,尽管你可能不熟悉功能指针的细节,但我认为你仍然是一个有价值的候选人,因为你知道你在认识到这一过程中做出的权衡。设计过程。
答案 3 :(得分:5)
答案 4 :(得分:3)
我会说它们在任何环境中都是有益的(在速度方面),而不仅仅是嵌入式。这个想法是,一旦指针指向正确的函数,就不需要进一步的决策逻辑来调用该函数。
答案 5 :(得分:2)
是的,它们很有用。我不确定面试官会得到什么。基本上,如果嵌入或不嵌入系统则无关紧要。除非你有严重限制的堆栈。
答案 6 :(得分:1)
函数指针的一个负面部分是它们永远不会在调用中内联。这可能是有益的,也可能不是有益的,这取决于您是在编译速度还是大小。如果是后者,它们应该与正常的函数调用没有区别。
答案 7 :(得分:1)
函数指针的另一个缺点(关于虚函数,因为它们只是核心级别的函数指针):
制作内联和&& virtual将强制编译器创建相同函数的外联副本。这将增加最终二进制文件的大小(假设已经大量使用它)。
经验法则:1:不要内联虚拟呼叫
答案 8 :(得分:1)
这是一个棘手的问题。有些行业禁止使用指针。
答案 9 :(得分:0)
让我们看看......
速度(比如我们在ARM上):然后(理论上):
(正常函数调用ARM指令大小)< (功能指针调用设置指令大小)
由于它们是设置函数指针调用的附加级别的间接,因此它将涉及额外的ARM指令。
PS:正常函数调用:使用BL设置的函数调用。
PSS:不知道它们的实际尺寸,但应该很容易验证。