2010-04-08 3 views
2

Je dois allouer une structure depuis une autre fonction, évidemment en utilisant des pointeurs. J'ai regardé ce problème pendant des heures et j'ai essayé de le résoudre d'un million de façons différentes.Malloc à l'intérieur d'une autre fonction

C'est un exemple de code (très simplifié):

... 
some_struct s; 
printf("Before: %d\n", &s); 
allocate(&s); 
printf("After: %d\n", &s); 
... 

/* The allocation function */ 
int allocate(some_struct *arg) { 

arg = malloc(sizeof(some_struct)); 
printf("In function: %d\n", &arg); 

return 0; 
} 

Cela ne me donne la même adresse avant et après l'attribution d'appel:

Before: -1079752900 
In function: -1079752928 
After: -1079752900 

Je sais qu'il est probablement parce qu'il fait une copie dans la fonction, mais je ne sais pas comment réellement travailler sur le pointeur que j'ai donné comme argument. J'ai essayé de définir some_struct * s au lieu de some_struct, mais pas de chance. J'ai essayé avec:

int allocate(some_struct **arg) 

qui fonctionne très bien, mais d'après la cession (le doit être modifiée fonction allouer ainsi) Je ne peux pas changer la déclaration, et il doit être * arg .. Et ce serait plus correct si je dois juste déclarer un_struct s .. Pas un_struct * s. Le but de la fonction d'allocation est d'initialiser un struct (un some_struct), qui inclut également l'allouer.

Encore une chose que j'ai oublié de mentionner. Le retour 0 dans la fonction d'allocation est réservé pour certains messages d'état et je ne peux donc pas retourner l'adresse en utilisant ceci.

+1

Quel est le véritable but de la fonction 'allocate'? – Earlz

+1

Si vous devez passer un '' some_struct', et allouer de la mémoire dans la fonction à assigner, l'affectation est impossible. Pourriez-vous poster plus de détails sur la mission, et à peu près ce que vous êtes censé apprendre? –

+0

Oh, et quand vous postez plus de détails, la bonne chose à faire ici est d'éditer votre question pour les inclure. Vous semblez être nouveau ici, et probablement ne connaissez pas l'étiquette SO. –

Répondre

1

Je doute fortement c'est ce que votre professeur avait esprit, mais vous pouvez tricher en utilisant une série de conversions de type juridique.

int allocate(some_struct *arg) 
    /* we're actually going to pass in a some_struct ** instead. 
     Our caller knows this, and allocate knows this. */ 
    { 
     void *intermediate = arg; /* strip away type information */ 
     some_struct **real_deal = intermediate; /* the real type */ 
     *real_deal = malloc(sizeof *real_deal); /* store malloc's return in the 
               object pointed to by real_deal */ 
     return *real_deal != 0; /* return something more useful than always 0 */ 
    } 

Ensuite, votre interlocuteur fait la même chose:

{ 
     some_struct *s; 
     void *address_of_s = &s; 
     int success = allocate(address_of_s); 
     /* what malloc returned should now be what s points to */ 
     /* check whether success is non-zero before trying to use it */ 
    } 

Cela repose sur une règle en C qui dit tout pointeur vers un objet peut être implicitement converti en un pointeur vide, et vice-versa, sans perte. Notez que cela est formellement indéfini, mais il est tout sauf sûr de travailler. Alors que toute valeur de pointeur d'objet est nécessaire pour pouvoir convertir en void* et revenir sans perte, il n'y a rien dans le langage qui garantit qu'un some_struct* peut stocker un some_struct** sans perte. Mais il a une très forte probabilité de travailler très bien.

Votre professeur ne vous a donné d'autre choix que d'écrire du code formellement illégal. Je ne vois pas que vous ayez d'autre choix que de "tricher" comme ça.

+0

Merci. Cela semble fonctionner, le code n'est pas joli. Oui, je trouve ça un peu bizarre aussi. La fonction doit aussi retourner 0, comme j'ai oublié de le mentionner. Mais je me souviens de pthreads .. Ils fonctionnent comme ça, autant que je sache. Vous initialisez certains attributs comme celui-ci: pthread_attr_t attr; pthread_attr_init (&attr); puis que vous utilisez est comme argument pour appeler un pthread_create. Cette initialisation alloue la struct pthread_attr_t. – Casper

+0

J'ai le sentiment lancinant que les spécifications de la cession se sont tronqués quelque part, que ce soit à la fin de l'enseignant ou de Casper Je voudrais sérieusement revérifier avec l'enseignant sur l'intention de la tâche avant d'y retourner. –

+0

Oui, je pense que j'ai mal compris le devoir .. Ce problème en est juste une partie. être un argument de some_struct * arg Peut-être que je suis supposé allouer les données non pas à l'intérieur de la fonction, mais alors la fonction n'a aucun but Hm .. C'est bizarre. – Casper

0

Vous ne pouvez pas le faire de cette façon. Vous ne pouvez pas déclarer une structure par valeur, puis la modifier par adresse.

+1

Je souhaite parfois que '& foo = ...' soit une syntaxe valide. : P – Earlz

0
some_struct *s; 
printf("Before: %d\n", s"); 
allocate(&s); 
printf("After: %d\n", s"); 
... 

/* The allocation function */ 
int allocate(some_struct **arg) { 

*arg = malloc(sizeof(some_struct)); 
printf("In function: %d\n", *arg"); 

return 0; 
} 

Vous devez modifier la valeur pointée pour la structure. Vous avez donc besoin d'un autre niveau d'indirection, vous devez donc envoyer un pointeur vers le pointeur struct.

+0

Il a précisé qu'il n'est pas autorisé à le faire. –

+0

@Jason, alors ce n'est pas possible. – Earlz

+0

@Earlz: Je pense que David avait la réponse que son professeur recherchait? –

4

En général, je retourne le pointeur de allocate:

void * allocate() 
{ 
    void * retval = malloc(sizeof(some_struct)); 
    /* initialize *retval */ 
    return retval; 
} 

Si vous voulez le retourner dans un paramètre, vous devez passer un pointeur sur le paramètre. Comme il est un pointeur vers un some_struct, vous devez passer un pointeur vers un pointeur:

void allocate (some_struct ** ret) 
{ 
    *ret = malloc(sizeof(some_struct)); 
    /* initialization of **ret */ 
    return; 
} 

être appelé comme

some_struct *s; 
allocate(&s); 
0

Eh bien, C utilise le passage par valeur, ce qui signifie que leurs fonctions obtiennent copies de leurs arguments, et les modifications apportées à ces copies n'affectent pas l'original dans l'appelant.

/* The allocation function */ 
int allocate(some_struct *arg) { 

arg = malloc(sizeof(some_struct)); 
printf("In function: %d\n", &arg"); 

return 0; 
} 

Ici vous passez l'adresse de votre some_struct s. Ensuite, vous supprimez cette adresse et la remplacez par ce qui a été retourné par malloc. Ensuite, vous revenez, et la valeur de retour de malloc est perdue pour toujours, et vous avez fui la mémoire. Et votre some_struct n'a pas été changé. Il a toujours le nombre aléatoire initialisé, que vous avez imprimé.

Si vous ne pouvez pas modifier la signature de la fonction d'allocation, elle ne pourra jamais être utile. Il doit soit prendre l'adresse d'un pointeur, afin qu'il puisse modifier la valeur de ce pointeur, soit renvoyer un pointeur que votre interlocuteur peut ranger.

1
int func(some_struct *arg) { 
    arg = malloc(sizeof(some_struct)); 
    ... 
} 

Ici vous suffit de lui assigner le résultat de malloc à la variable arg locale. les pointeurs sont passés par valeur dans C, une copie du pointeur est passée à la fonction. Vous ne pouvez pas changer le pointeur de l'appelant de cette façon. Gardez à l'esprit la différence dans un pointeur et à quoi il renvoie.

Vous avez différentes options:

retour le pointeur de la fonction:

some_struct *func(void) { 
    arg = malloc(sizeof(some_struct)); 
    ... 
    return arg; 
} 
... 
some_struct *a = func(); 

Répartir la structure de l'appelant:

int func(some_struct *arg) { 
    ... 
    arg->something = foo; 

} 
... 
some_struct a; 
func(&a); 

Ou allouent dynamiquement

some_struct *a = malloc(sizeof *a); 
func(a); 

l'aide d'un pointeur vers les appelants Pointer:

int func(some_struct **arg) { 
    *arg = malloc(sizeof **arg); 

} 
... 
some_struct *a; 
func(&a); 

Utilisez une variable globale (laid ..)

some_struct *global; 
int func(void) { 
    global = malloc(sizeof *global); 

} 
... 
some_struct *a; 
func(); 
a = global;