[英]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
?
具體來說,如果func()
一次被調用,我不想從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()僅在調用函數作用域中被調用一次感興趣...
相反,它確保在程序啟動時(或在加載該庫的一部分時)恰好調用一次該函數。
如果您需要控制何時以正確的方式初始化函數本地的靜態變量的初始化,則可以使用靜態變量來跟蹤是否已使用自己的靜態變量調用了一個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();
}
您可以將函數包裝到另一個函數中,該函數將檢查靜態變量並僅在之前未調用過func
時才調用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
標志,因此將運行該函數。 對於任何后續調用,該標志已經設置,因此將不會調用該函數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.