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

Is there a way, in C, to ensure a function is called only once without pthread_once?

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

具体来说,我不想return早于func()如果已经调用过一次,我有兴趣编译-时间保证 func() 仅从调用函数范围 调用 一次 - 即类似于 C++ 处理上述代码的方式。
(换句话说,上面的代码对于 C++ 编译器是合法的,它确保 func() 只被调用一次 - 在没有 [=15= 的 C 中是否有等效的方法来做到这一点]?)

编辑:
我在原来的 post 中并没有完美地表达这一点:我正在寻找一个不涉及 wrapper/helper 函数或变量的解决方案;也就是说,我很想知道 C 语言 中是否有一个 结构允许这种情况的处理方式与 C++ 中的处理方式相同。 jxh 的解决方案最适合使用 gcc 扩展。

您可以将您的函数包装在另一个函数中,该函数检查静态变量并仅在之前未被调用时才调用 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 标志未设置,因此函数将为 运行。对于任何后续调用,该标志已设置,因此不会调用该函数。

无法利用静态变量初始化

您尝试利用静态变量初始化将不会奏效。 C 只允许 static 变量用常量初始化,所以函数调用失败。

在程序启动(或库加载)时调用一个函数

目前还不清楚为什么要一次性调用,但如果在程序启动时就可以,那么有一个 GCC 特定的解决方案。 您可以将 constructor 属性分配给函数。

#include <stdio.h>

__attribute__((constructor)) 
void func()
{
  puts(__func__);
}

int main () {}

此建议不符合您的具体要求:

I'm interested in compile-time assurance that func() is only called once from a calling function scope ...

相反,它确保函数在程序启动时(或加载它所属的库时)恰好被调用一次。

使用静态变量作为守卫

如果您需要控制何时调用函数,就像初始化函数局部静态变量的初始化方式一样,那么您可以使用静态变量来跟踪您的一次性函数是否已被调用自己的静态变量。其他答案已经描述了如何完成这个,但为了完整起见:

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();
}