我有一个用于嵌入式系统(foo.c
,foo.h
)的C模块,该模块包含从API角度来看范围内的函数my_driver_fn()
(例如,不在foo
的公共标头中:任何其他通过#include "foo.h"
使用其API的代码不应被允许调用此函数)。假设my_driver_fn()
是可重入的。
但是,foo
使用库libdostuff
,该库需要用一些用户提供的回调函数(体系结构/硬件特定的东西)进行初始化,以使其在任何平台上都能正常工作。在foo
中,上面提到的my_driver_fn
将是有问题的功能之一……需要libdostuff
,但不是使用foo
的任何人。
对于这些回调函数(my_driver_fn()
在static
中被声明为foo.c
,这是否是错误的形式,危险的,不利的,以任何方式妨碍编译器或编译器可以利用的未定义行为?给定它的地址提供给libdostuff
并且被“间接”调用(尽管从不直接)?
注意:我碰巧同时写了foo
和libdostuff
,我想知道让用户提供的函数在链接时纯粹解析还是传递给extern
更有意义? libdostuff
通过初始化函数中提供的用户提供的回调表(例如libdostuff_init(CallbackTable *user_callbacks)
,其中CallbackTable
具有要初始化为指向my_driver_fn
的函数指针)
我认为这是一种好习惯。 static
是指名称的可见性,仅此而已。
如果其他翻译单元不需要使用该名称,则将其标记为static
可以减少在外部可见函数的“名称空间”中发生冲突的风险。
这是一种好习惯,并且没有不良行为,例如行为不明确。
使用static
的原因很多,例如减少名称空间污染和避免意外/故意呼叫。但是主要的原因实际上是私有封装设计和自文档代码-将函数保留在它们所属的模块中,因此外界无需担心如何以及何时调用它们。他们只应关心在公共头文件中声明的外部链接功能。
例如,这正是您在嵌入式系统中设计ISR的方式。应该始终将它们声明为static
,并将它们放置在控制ISR相关硬件的驱动程序内部。与ISR的所有通信,包括竞争条件保护,都应封装在该驱动程序中。调用应用程序永远不会直接与ISR对话,也不必担心重入等问题。与DMA缓冲区相同。
作为示例,我总是为我的所有MCU项目设计一个循环计时器驱动程序,调用者的API可让他们注册简单的回调函数。然后,计时器驱动程序会在ISR内部从计时器中调用计时器。然后,它可以用作需要计时器的所有低优先级任务的通用计时器:延迟,去弹跳等。然后,调用方只需要负责任地使回调函数保持最小,并以毫秒为单位指定时间,但是调用方不了解或不关心计时器硬件,并且计时器硬件不知道其执行的回调。双向松动。此计时器API还可作为HAL,因此您可以将代码移植到另一个MCU,而无需更改调用者代码-仅更改基础驱动程序。