Возвращает `struct` из функции в C

Сегодня я преподавал пару друзей, как использовать C struct s. Один из них спросил, можете ли вы вернуть struct из функции, на которую я ответил: "Нет! Вместо этого вы возвращаете указатели на динамически malloc ed struct."

Исходя из того, кто прежде всего С++, я ожидал, что не сможет вернуть struct значениями. В С++ вы можете перегрузить operator = для своих объектов и имеет смысл иметь функцию для возврата объекта по значению. В C, однако, у вас нет этой опции, и поэтому мне стало интересно, что делает компилятор. Рассмотрим следующее:

struct MyObj{
    double x, y;
};

struct MyObj foo(){
    struct MyObj a;

    a.x = 10;
    a.y = 10;

    return a;
}        

int main () {

    struct MyObj a;

    a = foo();    // This DOES work
    struct b = a; // This does not work

    return 0;
}    

Я понимаю, почему struct b = a; не должен работать - вы не можете перегружать operator = для своего типа данных. Как получается, что a = foo(); компилируется отлично? Означает ли это что-то другое, кроме struct b = a;? Может быть, задан вопрос: что именно делает оператор return в сочетании с знаком =?

[править]: Хорошо, я просто указал, что struct b = a - синтаксическая ошибка - это правильно, и я идиот! Но это еще более усложняет! Использование struct MyObj b = a действительно работает! Что мне здесь не хватает?

Ответ 1

Вы можете без проблем вернуть структуру из функции (или использовать оператор =). Это четко определенная часть языка. Единственная проблема со struct b = a заключается в том, что вы не указали полный тип. struct MyObj b = a будет работать нормально. Вы также можете передавать структуры в функции - структура точно такая же, как и любой встроенный тип для целей передачи параметров, возвращаемых значений и назначения.

Здесь простая демонстрационная программа, которая выполняет все три операции - передает структуру в качестве параметра, возвращает структуру из функции и использует структуры в операторах присваивания:

#include <stdio.h>

struct a {
   int i;
};

struct a f(struct a x)
{
   struct a r = x;
   return r;
}

int main(void)
{
   struct a x = { 12 };
   struct a y = f(x);
   printf("%d\n", y.i);
   return 0;
}

Следующий пример почти такой же, но использует встроенный тип int для демонстрационных целей. Две программы имеют одинаковое поведение в отношении передачи по значению для передачи параметров, назначения и т. Д.:

#include <stdio.h>

int f(int x) 
{
  int r = x;
  return r;
}

int main(void)
{
  int x = 12;
  int y = f(x);
  printf("%d\n", y);
  return 0;
}

Ответ 2

При выполнении вызова, такого как a = foo();, компилятор может вытолкнуть адрес структуры результатов в стек и передать его как "скрытый" указатель на функцию foo(). Фактически, это может стать чем-то вроде:

void foo(MyObj *r) {
    struct MyObj a;
    // ...
    *r = a;
}

foo(&a);

Однако точная реализация этого зависит от компилятора и/или платформы. Как отмечает Карл Норум, если структура достаточно мала, ее можно даже полностью вернуть в регистр.

Ответ 3

Строка struct b не работает, потому что это синтаксическая ошибка. Если вы расширите его, включив тип, он будет работать просто отлично

struct MyObj b = a;  // Runs fine

Что C здесь делает, по существу является memcpy от исходной структуры до места назначения. Это справедливо как для назначения, так и для возврата значений struct (и действительно любого другого значения в C)

Ответ 4

да, возможно, мы сможем передать структуру и структуру возврата. Вы были правы, но на самом деле вы не передали тип данных, который должен быть похож на эту структуру MyObj b = a.

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

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

#include<stdio.h>
struct marks{
    int maths;
    int physics;
    int chem;
};

struct marks deviation(struct marks student1 , struct marks student2 );

int main(){

    struct marks student;
    student.maths= 87;
    student.chem = 67;
    student.physics=96;

    struct marks avg;
    avg.maths= 55;
    avg.chem = 45;
    avg.physics=34;
    //struct marks dev;
    struct marks dev= deviation(student, avg );
    printf("%d %d %d" ,dev.maths,dev.chem,dev.physics);

    return 0;
 }

struct marks deviation(struct marks student , struct marks student2 ){
    struct marks dev;

    dev.maths = student.maths-student2.maths;
    dev.chem = student.chem-student2.chem;
    dev.physics = student.physics-student2.physics; 

    return dev;
}

Ответ 5

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

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

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

Ответ 6

Вы можете назначать структуры в C. a = b;. Это синтаксис.

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

Ответ 7

Нет необходимости передавать структуру. Он будет передан значением

Но что, если структура содержит любой член, который имеет адрес локальной переменной

struct emp {
    int id;
    char *name;
};

struct emp get() {
    char *name = "John";

    struct emp e1 = {100, name};

    return (e1);
}

int main() {

    struct emp e2 = get();

    printf("%s\n", e2.name);
}

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

Где, когда e1.id будет совершенно корректным, поскольку его значение будет скопировано в e2.id

Итак, мы всегда должны стараться не возвращать адреса локальной памяти функции.

Все, что malloced может быть возвращено как и когда захочет

Ответ 8

struct emp {
    int id;
    char *name;
};

struct emp get() {
    char *name = "John";

    struct emp e1 = {100, name};

    return (e1);
}

int main() {

    struct emp e2 = get();

    printf("%s\n", e2.name);
}

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

Ответ 9

Адрес struct var e2 помещается в стек как вызываемый аргумент arg, и там присваиваются значения. Фактически, get() возвращает адрес e2 в регистре eax. Это работает как звонок по ссылке.