1

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()等效的方法来做到这一点?)Cpthread_once

编辑:
我在原始帖子中并没有理想地表达这一点:我正在寻找一个不涉及包装器/帮助器函数或变量的解决方案;即我很想知道语言中是否有一个结构C允许这种情况的处理方式与它的处理方式相同C++。jxh 的解决方案最适合,利用gcc扩展。

4

3 回答 3

4

无法利用静态变量初始化

您尝试利用静态变量初始化将不起作用。C 只允许static用常量初始化变量,所以函数调用被淘汰了。

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

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

#include <stdio.h>

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

int main () {}

此建议不执行您的具体要求:

我对编译时保证 func() 仅从调用函数范围调用一次感兴趣...

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

使用静态变量作为保护

如果您需要以初始化函数本地静态变量的确切方式来控制函数何时被调用,那么您可以使用静态变量来跟踪您的 one shot 函数是否已使用其自己的静态变量被调用。其他答案已经描述了如何实现这一点,但为了完整起见:

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();
}
于 2019-02-12T22:42:46.720 回答
2

您可以将函数包装在另一个函数中,该函数检查静态变量并func仅在之前未调用时调用 -

static int func_internal() {
    ...
}

int func() {
    static int guard = 0;
    if (guard)
        return 0;
    guard = 1;
    return func();
}

func现在,只向其他模块公开。

于 2019-02-12T22:39:26.987 回答
1

你可以用一个static标志来做到这一点。

// main.c

int func()
{
  return 42;
}

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

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

于 2019-02-12T22:41:13.863 回答