在C
,有没有办法确保函数只调用一次而不使用pthread_once
?
以下在C++
中工作,但显然不在C
中,因为静态变量的初始化必须是常量(因为我解释编译错误)
// main.c
int func()
{
return 42;
}
int main( int argc, char* argv[] )
{
static int i = func();
return 0;
}
我以为使用逗号运算符可能会解决这个问题,但这也不起作用:
// main.c
int func()
{
return 42;
}
int main( int argc, char* argv[] )
{
static int i = ( func(), 42 );
return 0;
}
编译两者导致以下编译错误:
> gcc -g main.c
main.c: In function 'main':
main.c:10:18: error: initializer element is not constant
有没有办法绕过这个并确保只调用一次函数(来自调用函数范围)而不使用pthread_once
?
具体来说,我不想早期从func()
返回,如果它已被调用一次,我对编译时保证func()
仅从调用函数范围调用一次感兴趣 - 即类似于C++
将如何处理上面的代码。
(换句话说,上面的代码对于C++
编译器来说是合法的,它确保func()
只被调用一次 - 在没有C
的pthread_once
中是否有相同的方法来执行此操作?)
编辑:
我没有在原帖中理想地说这个:我正在寻找一个不涉及包装器/辅助函数或变量的解决方案;即我很想知道C
语言中是否有一个构造允许这种情况等同于C++
中的处理方式。 jxh的解决方案最适合,使用gcc
扩展。
您尝试利用静态变量初始化将不起作用。 C只允许使用常量初始化static
变量,因此函数调用已经完成。
目前尚不清楚为什么要进行一次性调用,但如果可以在程序启动时进行,则可以使用GCC特定的解决方案。您可以将constructor
属性分配给该函数。
#include <stdio.h>
__attribute__((constructor))
void func()
{
puts(__func__);
}
int main () {}
此建议不会执行您的具体要求:
我对编译时保证func()只从调用函数范围调用一次感兴趣...
相反,它确保函数在程序启动时(或者当它是加载的库的一部分时)被调用一次。
如果你需要控制函数被调用的时间与函数本地静态变量的初始化完全一致,那么你可以使用静态变量来跟踪你的单击函数是否已经被自己的静态变量调用。其他答案已经描述了如何实现这一点,但为了完整性:
void caller_of_func()
{
static bool func_already_called;
if (!func_already_called) {
func();
func_already_called = true;
}
/*...*/
}
实现目标的另一种方法是通过函数指针调用函数。对函数的初始调用将完成实际工作,然后将函数指针切换为指向不执行任何操作的函数。
void nothing_func(int *x);
void initial_func(int *x);
void (*func)(int *x) = initial_func;
void initial_func(int *x) {
*x = 42;
puts(__func__);
func = nothing_func;
}
void nothing_func(int *x) {
puts(__func__);
}
void foo(void) {
static int x;
func(&x);
printf("%s: %d\n", __func__, x);
++x;
}
int main(void) {
foo();
foo();
}
你可以将你的函数包装在另一个检查静态变量的函数中,并且只有在之前没有调用它时才调用func
-
static int func_internal() {
...
}
int func() {
static int guard = 0;
if (guard)
return 0;
guard = 1;
return func();
}
现在,只将func
暴露给其他模块。
你可以用static
旗帜做到这一点。
// main.c
int func()
{
return 42;
}
int main( int argc, char* argv[] )
{
static int initialized = 0;
if(!initialized) {
func();
initialized = 1;
}
}
第一次通过调用代码时,未设置initialized
标志,因此将运行该函数。对于任何后续调用,该标志已经设置,因此不会调用该函数。