Malloc внутри другой функции - PullRequest
2 голосов
/ 08 апреля 2010

Я должен выделить структуру из другой функции, очевидно, используя указатели. Я часами смотрел на эту проблему и пытался решить ее миллионами разных способов.

Это пример кода (очень упрощенный):

...
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;
}

Это дает мне один и тот же адрес до и после вызова выделения:

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

Я знаю, что это, вероятно, потому что это делает копию в функции, но я не знаю, как на самом деле работать с указателем, который я дал в качестве аргумента. Я попытался определить some_struct * s вместо some_struct s, но безуспешно. Я пробовал с:

int allocate(some_struct **arg)

, который работает просто отлично (необходимо также изменить функцию allocate), НО в соответствии с назначением я НЕ МОГУ изменить объявление, и оно ДОЛЖНО быть * arg .. должны объявить some_struct *. Не some_struct * s. Назначение функции выделения - инициализация структуры (a some_struct), которая также включает ее выделение.

Еще одна вещь, которую я забыл упомянуть. Возвращаемый 0 в функции allocate зарезервирован для некоторых сообщений о состоянии, и поэтому я не могу вернуть адрес, используя это.

Ответы [ 6 ]

4 голосов
/ 08 апреля 2010

Обычно я бы возвращал указатель от allocate:

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

Если вы хотите вернуть его в параметре, вы должны передать указатель на параметр. Поскольку это указатель на some_struct, вы должны передать указатель на указатель:

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

будет называться

some_struct *s;
allocate(&s);
1 голос
/ 08 апреля 2010

Я очень сомневаюсь, что это имел в виду ваш учитель, но вы можете обмануть, используя серию законных преобразований типов.

   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 */
   }

Тогда ваш абонент делает то же самое:

   {
      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 */
   }

Это опирается на правило в C, которое гласит, что любой указатель на объект может быть неявно преобразован в указатель void, и наоборот, без потерь.

Обратите внимание, что формально это не определено, но это почти наверняка сработает. В то время как любое значение указателя объекта должно быть в состоянии преобразовать в void* и обратно без потерь, в языке нет ничего, что гарантировало бы, что some_struct* может хранить some_struct** без потерь. Но у него очень высокая вероятность того, что он будет работать нормально.

Ваш учитель не дал вам другого выбора, кроме как написать формально незаконный код. Я не вижу, что у вас есть какой-то другой вариант, кроме как "обман", как это.

1 голос
/ 08 апреля 2010
int func(some_struct *arg) {
    arg = malloc(sizeof(some_struct));
    ... 
}

Здесь вы просто присваиваете результат malloc локальной переменной arg.указатели передаются по значению в C, копия указателя передается в функцию.Вы не можете изменить указатель вызывающего абонента таким образом.Имейте в виду разницу в указателе и на что он указывает.

У вас есть различные варианты:

Возврат указателя из функции:

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

Выделите структуру в вызывающей стороне:

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

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

Или выделите ее динамически

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

Используя указатель на указатель вызывающей стороны:

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

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

Использовать глобальную переменную (некрасиво ..)

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

}
 ... 
some_struct *a;
func();
a = global;
0 голосов
/ 08 апреля 2010

Ну, C использует передачу по значению, что означает, что функции получают копий своих аргументов, и любые изменения, внесенные в эти копии, не влияют на оригинал в вызывающей стороне.

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

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

return 0;
}

Здесь вы передаете адрес вашего some_struct s. Затем вы отбрасываете этот адрес и заменяете его тем, что было возвращено malloc. Затем вы возвращаетесь, и возвращаемое значение malloc теряется навсегда, и у вас просачивается память. И ваши some_struct s не были изменены. У него все еще есть случайное число, к которому оно было инициализировано, которое вы распечатали.

Если вы не можете изменить сигнатуру функции allocate, она никогда не будет полезна. Он должен либо взять адрес указателя, чтобы он мог изменить значение этого указателя, либо он должен вернуть указатель, который ваш вызывающий абонент может убрать.

0 голосов
/ 08 апреля 2010
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;
}

Вам необходимо изменить указанное значение для структуры. Так что вам нужен другой уровень косвенности, поэтому вы должны отправить указатель на указатель структуры.

0 голосов
/ 08 апреля 2010

Вы не можете сделать это таким образом. Вы не можете объявить структуру по значению, а затем изменить ее по адресу.

...