Вопрос о возвратных функциях в C

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

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

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

Ответ 1

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

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

Фундаментальное различие заключается в жизни и ответственности.

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

Ответ 4

Оба являются законными, оба работают.

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

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

Ответ 5

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

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);

Ответ 6

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

Ответ 7

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

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

Вам нужно явно освободить память на C, у него нет сборщика мусора, такого как Java.

Ответ 8

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

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

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

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

Ответ 9

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

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

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

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

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

Ответ 10

Первый:

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