En C
, existe-t-il un moyen de s'assurer qu'une fonction n'est appelée qu'une seule fois sans utiliser pthread_once
?
Ce qui suit fonctionne en C ++ code > mais apparemment pas en
C
car l'initialisation d'une variable statique doit se faire sur une constante (comme j'interprète l'erreur de compilation)
> gcc -g main.c main.c: In function 'main': main.c:10:18: error: initializer element is not constant
J'ai pensé utiliser L'opérateur virgule peut contourner ce problème, mais cela ne fonctionne pas non plus:
// main.c int func() { return 42; } int main( int argc, char* argv[] ) { static int i = ( func(), 42 ); return 0; }
La compilation des deux résultats entraîne l'erreur de compilation suivante:
// main.c int func() { return 42; } int main( int argc, char* argv[] ) { static int i = func(); return 0; }
3 Réponses :
Vous pouvez encapsuler votre fonction dans une autre fonction qui vérifie une variable statique et n'appelle func
que si elle n'a pas été appelée auparavant en tant que -
static int func_internal() { ... } int func() { static int guard = 0; if (guard) return 0; guard = 1; return func(); }
Maintenant, exposer uniquement func
aux autres modules.
Vous pouvez le faire avec un indicateur statique
.
// main.c int func() { return 42; } int main( int argc, char* argv[] ) { static int initialized = 0; if(!initialized) { func(); initialized = 1; } }
La première fois que vous utilisez le code d'appel, l'indicateur initialisé
est désactivé , donc la fonction sera exécutée. Pour tous les appels ultérieurs, l'indicateur est déjà défini et la fonction ne sera donc pas appelée.
Votre tentative de tirer parti de l'initialisation des variables statiques ne fonctionnera pas. C autorise uniquement l'initialisation de la variable static
avec des constantes, donc un appel de fonction est sorti.
La raison pour laquelle vous voulez un appel unique n'est pas claire, mais si vous pouvez le faire au démarrage du programme, il existe une solution spécifique à GCC.
Vous pouvez attribuer l'attribut constructor
à la fonction.
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(); }
Cette suggestion n'exécute pas votre demande spécifique:
Je suis intéressé par l'assurance à la compilation que func () n'est appelée qu'une seule fois à partir d'une portée de fonction appelante ...
Au lieu de cela, il garantit que la fonction est appelée exactement une fois lorsque le programme démarre (ou lorsque la bibliothèque dont elle fait partie est chargée).
Si vous avez besoin de contrôler le moment où la fonction est appelée de la même manière que l'initialisation d'une variable statique locale à une fonction est initialisée, vous pouvez utiliser une variable statique pour savoir si votre fonction one shot a déjà été appelée avec sa propre variable statique. D'autres réponses ont déjà décrit comment y parvenir, mais par souci d'exhaustivité:
void caller_of_func() { static bool func_already_called; if (!func_already_called) { func(); func_already_called = true; } /*...*/ }
Une autre façon d'atteindre votre objectif serait de appeler la fonction via un pointeur de fonction. L'appel initial de la fonction ferait le vrai travail, puis basculerait le pointeur de la fonction pour qu'il pointe vers une fonction qui ne fait rien.
#include <stdio.h> __attribute__((constructor)) void func() { puts(__func__); } int main () {}
Merci pour la réponse complète. Votre première phrase et paragraphe explicatif, précisément, est la réponse que je cherchais.
Je n'avais pas pensé à l'idée du pointeur de fonction. Maintenant c'est cool!
Votre programme est-il multithread ou monothread?
@ user3386109 - célibataire
@StoneThrow alors pourquoi ne pas avoir un booléen qui marque quand il a été appelé? Ce serait une variable globale ou statique dans la fonction elle-même.
Et
i
est-il en fait une variable à l'intérieur demain
, ou est-ce quei
est censé être dans une autre fonction? Notez que les variables déclarées dansmain
n'ont jamais besoin du mot-cléstatic
, car elles existent toujours pendant toute la durée de vie du programme. Compte tenu du code de la question, la solution est donc simplement de supprimer le mot-cléstatic
.@JustinMeiners - cette question est dans une perspective de curiosité académique: c'est-à-dire existe-t-il une fonctionnalité du langage
C
qui permet l'équivalent de la façon dontC ++
gère cette situation. La réponse est apparemment non, mais comme les répondants l'ont souligné, l'équivalent peut être obtenu avec une fonction wrapper et une variable «guard». Un peu de mon mal de ne pas être plus clair, je cherchais une solution de langue native, mais les répondants ont répondu à ma question comme indiqué, donc ils méritent d'être félicités.Si
i
est une variable statique dans une autre fonction, alors une variable "guard" est la bonne solution.En réponse à la modification. En plus d'être non portable, la solution
constructeur
de jxh ne fournit pas réellement de solution au problème comme indiqué. Plus précisément, il ne peut pas être utilisé pour initialiser une variablestatique
déclarée dans la portée de la fonction.@ user3386109: Je crois que l'OP essayait d'utiliser la sémantique d'initialisation de variable statique comme mécanisme pour forcer un appel unique d'une sous-fonction lorsque la fonction externe est appelée.
@jxh - Correct; merci de l'exprimer succinctement. J'ai fait un mauvais travail de formulation.
@StoneThrow cool, j'ai pensé que le fait que vous connaissiez pthread_once ne correspondait pas à la question de savoir si vous pouviez définir un booléen :)