вопрос касательно функций возврата в C - PullRequest
10 голосов
/ 07 сентября 2011

Я - программист Java, изучающий C. У меня есть вопрос, касающийся функций. Каковы различия между этим:

main()
{
    struct person myperson;

    myperson = myfunction();

    return;
}

struct person myfunction()
{
     struct person myPerson;
     myPerson.firstname = "John";
     myPerson.lastname = "Doe";
     return myPerson;
}

VS

main()
{
    struct person *myperson;

    myperson = myfunction();

    return;
}

struct person* myfunction()
{
     struct person *myPerson;
     myPerson = malloc(sizeof(struct person));
     myPerson->firstname = "John";
     myPerson->lastname = "Doe";
     return myPerson;
}

Законны ли они в Си? И я бы выбрал одно над другим. Большое спасибо, ребята!

Ответы [ 10 ]

7 голосов
/ 07 сентября 2011

первый пример кода:
вы создаете структуру в myfunction () в вашем стеке и возвращаете ее. затем вы создаете другую структуру стека и копируете с первого на второй. первый уничтожен. вторая будет автоматически уничтожена, когда вы выйдете из области видимости.
2 структуры были фактически созданы.

второй пример кода:
вы создаете структуру в myfunction (), а затем копируете только адрес. структура в main будет фактически той же самой структурой.
только здесь создается одна структура .

оба примера кода работают, но для последующего вам придется явно освободить память , выделенную для структуры, чтобы избежать утечки памяти, но производительность должна быть лучше, поскольку вам не нужно копировать структура!

EDIT:
как упомянуто @Mat: это, конечно, игнорирует издержки malloc (), что не верно для небольших структур.

7 голосов
/ 07 сентября 2011

Первая версия размещает объект в стеке и возвращает его копию .Вторая версия создает объект в куче и возвращает на него указатель (это наиболее близко к ссылкам Java, за исключением того, что память не освобождается автоматически).Не следует забывать позже вызывать free() для возвращенного указателя.

Кстати, ваша основная функция плохаяЭто должно быть

int main(void)
{    
    ...
    return 0;
}

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

4 голосов
/ 07 сентября 2011

Я не уверен, что весь этот разговор о «куче» и «стеке» урезан до глубины языка, поэтому позвольте мне попробовать что-то более внутреннее.

Ваша первая версия использует только автоматическое распределение, что означает, что все переменные имеют автоматическое время жизни. То есть все переменные заканчивают свою жизнь в конце своей охватывающей области: myFunction создает локальную переменную типа struct person и возвращает копию этой переменной; функция main объявляет локальную переменную того же типа и присваивает ей результат вызова функции. В конце каждой области видны и локальные переменные.

Вторая версия использует динамическое или ручное распределение . Вы явно выделяете хранилище для переменной person с помощью вызова malloc(), и это хранилище будет выделяться до тех пор, пока кто-то не освободит (через free()). Поскольку вы никогда не освобождаете его, это фактически утечка памяти.

Принципиальным отличием является срок службы и ответственность .

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

3 голосов
/ 07 сентября 2011

Я бы на самом деле выбрал третий путь. Позвольте вызывающей стороне беспокоиться о предоставлении места для хранения (автоматически или динамически распределяемого):

void myfunction(struct person* myPerson)
{
     myPerson->firstname = "John";
     myPerson->lastname = "Doe";
}

Функция может быть вызвана либо с автоматически, либо с динамически назначаемой переменной:

struct person autoperson;
myfunction(&person);

struct person dynamic_person = malloc(sizeof struct person);
myfunction dynamic_person);
3 голосов
/ 07 сентября 2011

Оба легальны, оба работают.

1-я версия проще, вам не нужно иметь дело с распределением и освобождением памяти.

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

1 голос
/ 07 сентября 2011

Первый выделит struct person в стеке и передаст его копию обратно, затем освободит оригинал.Второй выделит его в куче и передаст указатель на место, которое было выделено, и не освободит его.

0 голосов
/ 07 сентября 2011

Первый:

main()
{
    // create a person struct on the stack
    struct person myperson;
    // copy the struct returned by myfunction to myperson.
    myperson = myfunction();
}

struct person myfunction()
{
    // create a person struct on the stack.
    struct person myPerson;
    myPerson.firstname = "John";
    myPerson.lastname = "Doe";
    // return the myPerson struct. After myFunction returns, the memory
    // holding the myPerson struct on the stack will be freed.
    return myPerson;
}

Второй:

main()
{
    // create a pointer to a person struct on the stack
    struct person *myperson;
    // assign the pointer returned by myfunction to myperson
    myperson = myfunction();
}

struct person* myfunction()
{
    // create a pointer to a person struct on the stack
    struct person *myPerson;
    // allocate memory for a person struct in dynamic memory and set myPerson
    // to point to that memory. This memory will remain valid until it's freed by
    // a call to the "free" function. Using malloc is much slower than creating
    // an object on the stack. There is also the added performance cost of
    // freeing the allocated memory at a later stage.
    myPerson = malloc(sizeof(struct person));  
    myPerson->firstname = "John";
    myPerson->lastname = "Doe";
    // return the myPerson pointer
    return myPerson;
}
0 голосов
/ 07 сентября 2011

В первом коде myPerson - это объект типа struct person, который управляется (*) самой реализацией.Во втором коде это объект типа struct person * (указатель на struct person).Во втором коде сам объект должен управляться программистом (malloc, realloc, free).

Кроме того, в первом коде сам объект копируется несколько разтогда как во втором коде «только» указатель копируется. Обычно указатель намного меньше объекта типа структуры.

Используйте 2-й подход, но не забудьте free объект.

Еще лучше создатьобъект в родительской функции и передать указатель на функции: sruct person *myfunction(struct person *data) { /* ... */ }

(*) с управлением объектом. Я имею в виду время его создания и удаления и прочее

0 голосов
/ 07 сентября 2011

Первая опция создает структуру в стеке, при возврате она копируется в вашу структуру, определенную в функции main().Также скопированы поля.Для больших структур это может быть дорогостоящей операцией.

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

Конечно, это зависит от ваших потребностей, но для более важных и долгоживущих объектов я бы выбрал второй вариант.Также я бы порекомендовал написать функции выделения / инициализации и соответствующую функцию освобождения.(см. ниже)

Проблема в том, что 2 строки, которые вы задали в myfunction(), недопустимы вне функции, так как они также создаются в стеке.Вы должны использовать strdup() или аналогичную функцию, чтобы сделать это без сохранения.Конечно, чтобы не допустить утечки памяти, вам нужно free() strdup ed указатели, как и с malloc().

0 голосов
/ 07 сентября 2011

Первый размещает переменные в стеке.Объект person из myfunction копируется из функции и возвращается, что является менее эффективным, но вы не можете получить утечку памяти, которая является хорошей.

Второй пример возвращает указатель (*) на объект personдинамически распределяется (с помощью malloc).Объект person, выделенный функцией malloc, никогда не будет уничтожен, если вы явно не вызовете для него функцию free (), а у вас - нет, поэтому у вас есть утечка памяти.не имеет сборки мусора, как Java.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...