在C中,有没有一种方法可以确保一个函数在没有pthread_once的情况下仅被调用一次?

时间:2019-02-12 22:31:11

标签: c static-variables variable-initialization

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中有没有{{1} }?)

编辑:
我没有在最初的帖子中理想地表达这一点:我在寻找一种不涉及包装器/帮助器函数或变量的解决方案。即我很想知道是否存在pthread_once语言的构造,该构造与在C中的处理方式等效。 jxh的解决方案最合适,它利用C++扩展名。

3 个答案:

答案 0 :(得分:3)

无法利用静态变量初始化

您尝试利用静态变量初始化将不起作用。 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();
}

答案 1 :(得分:2)

您可以将函数包装到另一个函数中,该函数将检查静态变量并仅在之前没有以{的形式调用过-

func

现在,仅将static int func_internal() { ... } int func() { static int guard = 0; if (guard) return 0; guard = 1; return func(); } 公开给其他模块。

答案 2 :(得分:1)

您可以使用static标志来完成此操作。

// main.c

int func()
{
  return 42;
}

int main( int argc, char* argv[] )
{
    static int initialized = 0;
    if(!initialized) {
        func();
        initialized = 1;
    }
}

第一次调用代码时,initialized标志未设置,因此该函数将运行。对于任何后续调用,该标志已经设置,因此将不会调用该函数。