2010-05-26 4 views
1

Ceci est mon premier message, alors s'il vous plaît soyez doux.Déclaration, attribution et affectation d'un tableau de pointeurs aux pointeurs de fonction

J'ai joué avec C de temps en temps dans le passé. Maintenant je suis arrivé au point où j'ai démarré un vrai projet (un moteur graphique 2D utilisant SDL, mais ce n'est pas pertinent pour la question), pour pouvoir dire que j'ai une réelle expérience du C. Hier, en travaillant sur le système d'événements, j'ai rencontré un problème que je ne pouvais pas résoudre.

Il y a cette typedef,


//the void parameter is really an SDL_Event*. 
//but that is irrelevant for this question. 
typedef void (*event_callback)(void); 

qui spécifie la signature d'une fonction à appeler sur les événements du moteur. Je veux être capable de supporter plusieurs event_callbacks, donc un tableau de ces callbacks serait une idée, mais je ne veux pas limiter le nombre de callbacks, donc j'ai besoin d'une sorte d'allocation dynamique. C'est là que le problème s'est posé. Ma première tentative est allé comme ceci:


//initial size of callback vector 
static const int initial_vecsize = 32; 
//our event callback vector 
static event_callback* vec = 0; 
//size 
static unsigned int vecsize = 0; 

void register_event_callback(event_callback func) { 
    if (!vec) 
     __engine_allocate_vec(vec); 
    vec[vecsize++] = func; //error here! 
} 

static void __engine_allocate_vec(engine_callback* vec) { 
    vec = (engine_callback*) malloc(sizeof(engine_callback*) * initial_vecsize); 
} 

Tout d'abord, j'ai omis une erreur de vérification ainsi que le code qui réaffecte le vecteur de rappel lorsque le nombre de callbacks dépasse la taille du vecteur. Toutefois, lorsque j'exécute ce code, le programme se bloque comme décrit dans le code. Je devine le défaut de segmentation mais je ne peux pas être sûr puisque aucune sortie n'est donnée. Je suppose également que l'erreur provient d'une compréhension quelque peu erronée sur la façon de déclarer et d'allouer un tableau de pointeurs à des pointeurs de fonction.

S'il vous plaît Stack Overflow, guide moi.

EDIT: Je n'arrive pas à comprendre comment mettre en retrait les blocs de code. C'est presque un peu embarrassant ...

EDIT: Ne l'oubliez pas. Vérifié la source de la page de certains autres messages =).

+0

Vous avez indenter le code de 4 espaces pour qu'il soit formaté correctement. – Artefacto

+1

Utilisez le bouton '101010' sur la page d'édition pour transformer le texte marqué en code. – sbi

+1

Vous avez besoin d'une double indirection dans __engine_allocate_vec – Artefacto

Répondre

1

A la ligne:

vec[vecsize++] = func; //error here! 

Qu'est-ce qui se passe si vecsize est >= initial_vecsize?

également __engine_allocate_ve ne fonctionne pas car il ne modifie que la copie locale de vec, vous devez changer la signature à un ** et passer l'argument avec &.

static void __engine_allocate_vec(engine_callback** vec)

__engine_allocate_vec(&vec);

+0

Merci! Je crois que vous étiez le premier. Je suis confus avec pass-by-val et pass-by-ptr parfois. Pourriez-vous expliquer pourquoi passer un pointeur sur un pointeur de fonction n'est pas suffisant? – manneorama

+0

Lorsque vous transmettez 'vec' par valeur, une copie de celui-ci est envoyée dans la fonction et est ensuite modifiée, la valeur d'origine de' vec' en dehors de la fonction reste inchangée.Pour corriger cela, au lieu de passer une copie de 'vec' vous passez un pointeur sur' vec'. A l'intérieur de la fonction, vous déréférerez ce pointeur pour modifier la variable vers laquelle il pointe. –

3

La fonction d'allocation doit être:

static void __engine_allocate_vec(engine_callback** vec) { 
    *vec = malloc(sizeof(engine_callback) * initial_vecsize); 
} 

puis:

if (!vec) 
    __engine_allocate_vec(&vec); 

Notez que le décalage de type pointeur dans votre fonction allocation initiale aurait été pris si vous avait omis le casting. De même, n'utilisez pas de noms contenant des caractères de soulignement doubles dans votre code - ils sont destinés à l'utilisation de l'implémentation.

0

Vous semblez être malloc -Ing basées sur sizeof(engine_callback*) plutôt que sizeof(engine_callback) ...

0

Votre fonction __engine_allocate_vec crée l'espace pour les nouveaux engine_callback s, mais il ne fait rien utile avec ce pointeur. (Il change sa version locale de vec, qui a été transmise par valeur - donc les changements ne la ramènent pas à l'appelant et le nom de l'argument cache le nom du global, donc ce n'est pas réglé non plus.) Donc quand ça revient , votre pointeur est toujours nul.

Essayez quelque chose comme ça ...

static void __engine_allocate_vec(engine_callback** vec) { 
    *vec = (engine_callback*) malloc(sizeof(engine_callback) * initial_vecsize); 
} 

Puis, en register_event_callback, passer &vec à la fonction au lieu de vec. Ou, faites annuler la fonction et laissez-la définir le global lui-même. Nah, oublie j'ai dit ça.

0

Tout d'abord, ne pas utiliser les traits de soulignement principaux pour les noms de variables ou de fonctions; ces noms sont réservés à la mise en œuvre.

Tout le monde a souligné la bonne façon d'allouer initialement le vecteur:

static void engine_allocate_vec(event_callback **vec) 
{ 
    *vec = malloc(sizeof **vec * initial_vecsize); 
} 

avis deux choses. Tout d'abord, je ne lance pas le résultat de malloc. Il n'est pas nécessaire (à partir de C89, de toute façon, les versions antérieures de C requièrent une distribution, comme C++), et il peut potentiellement supprimer un diagnostic du compilateur si vous oubliez d'inclure stdlib.h ou si vous n'avez pas de prototype pour malloc dans portée. Deuxièmement, j'appelle sizeof sur l'expression **vec, pas le type; puisque le type de l'expression **vec est event_callback, sizeof **vec renvoie le même résultat que sizeof (event_callback). Cela permet de réduire l'encombrement visuel, et il évite un bug assez courant qui s'insinue quand quelqu'un change le type de la variable, mais ne porte pas que le changement à travers l'expression sizeof dans l'appel malloc, comme

double *f; /* was originally declared as float, later changed to double */ 
... 
f = malloc(sizeof (float) * size); /* type change not carried through */ 

Notez que sizeof n'évalue pas son opérande (sauf s'il s'agit d'un VLA), vous n'avez donc pas à vous soucier de l'appeler sur une expression de pointeur non initialisée.

Ceci vous aide à créer le vecteur initial. Cependant, vous aimeriez pouvoir étendre le vecteur si nécessaire lors de l'enregistrement de plus de initial_vecsize rappels, n'est-ce pas? Si oui, permettez-moi de suggérer ce qui suit:

static int engine_allocate_vec(event_callback **vec, 
    size_t *currentSize, 
    size_t extent) 
{ 
    int success = 0; 
    /** 
    * Assign the result of realloc to a temporary; realloc returns NULL 
    * on failure, and we don't want to risk losing our pointer to the 
    * previously allocated memory. Similarly, we don't update *currentSize 
    * unless the call succeeds. Note that realloc(NULL, size) is equivalent 
    * to malloc(size). 
    */ 
    event_callback *tmp = realloc(*vec, sizeof *tmp * (*currentSize + extent)); 
    if (tmp != NULL) 
    { 
    *vec = tmp; 
    *currentSize += extent; 
    success = 1; 
    } 
    return success; 
} 

Ensuite, votre fonction registre devient:

/** 
* Adding vector_count variable to keep track of the number 
* of items in the vector as opposed to the physical size 
* of the vector. 
*/ 
static size_t vector_count = 0; 

void register_callback_event(event_callback func) 
{ 
    if (!vec) 
    { 
    /** 
    * Initial vector allocation 
    */ 
    if (!engine_allocate_vec(&vec, &vecsize, initial_vecsize)) 
    { 
     /* allocation failed; treating this as a fatal error */ 
     exit(0); 
    } 
    } 
    else if (vector_count == vecsize) 
    { 
    /** 
    * Need to extend the vector to accomodate 
    * the new callback. Double the vector size (i.e., 
    * extend it by the current vector size) 
    */ 
    if (!engine_allocate_vec(&vec, &vecsize, vecsize)) 
    { 
     /* extension failed - treating this as a fatal error*/ 
     free(vec); 
     exit(0); 
    } 
    } 

    vec[vector_count++] = func; 
} 
+0

Merci pour le taille-tip! N'avait pas vraiment considéré cela. La réallocation de la mémoire et la vérification des erreurs étaient déjà là. Je les ai laissés pour la brièveté. – manneorama