Вернуть `struct` из функции в C - PullRequest
145 голосов
/ 11 марта 2012

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

Исходя из того, кто в основном работает на C ++, я ожидал, что не смогу вернуть struct s по значениям. В C ++ вы можете перегрузить 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 действительно работает! Что мне здесь не хватает?

Ответы [ 9 ]

175 голосов
/ 11 марта 2012

Вы можете без проблем вернуть структуру из функции (или использовать оператор =).Это четко определенная часть языка.Единственная проблема с 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 тип для демонстрационных целей.Две программы имеют одинаковое поведение в отношении передачи по значению для передачи параметров, назначения и т. Д.: * 10101

#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;
}
29 голосов
/ 11 марта 2012

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

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

foo(&a);

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

13 голосов
/ 11 марта 2012

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

struct MyObj b = a;  // Runs fine

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

9 голосов
/ 30 декабря 2014

да, возможно, мы можем передать структуру и вернуть структуру. Вы были правы, но на самом деле вы не передали тип данных, который должен быть таким, как эта структура 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 голосов
/ 11 марта 2012

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

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

4 голосов
/ 11 марта 2012

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

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

3 голосов
/ 28 марта 2017

Нет проблем при возврате структуры. Будет передано по значению

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

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 () вернет, локальный адрес для имени будет освобожден. Итак, в вызывающей стороне, если мы пытаемся получить доступ к этому адресу, это может вызвать ошибку сегментации, так как мы пытаемся освободить адрес. Это плохо ..

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

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

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

1 голос
/ 14 мая 2017
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);
}

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

0 голосов
/ 21 июня 2019

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

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