从C中的函数返回`struct`

时间:2012-03-11 06:59:32

标签: c

今天我教了几个朋友如何使用C struct。其中一个问你是否可以从一个函数返回一个struct,我回答说:“不!你会返回指向动态malloc编辑struct的指针。”

来自主要使用C ++的人,我期望无法按值返回struct。在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 个答案:

答案 0 :(得分:173)

您可以从函数返回结构(或使用=运算符)而不会出现任何问题。它是语言中定义明确的部分。 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;
}

答案 1 :(得分:29)

当进行a = foo();之类的调用时,编译器可能会在堆栈上推送结果结构的地址,并将其作为“隐藏”指针传递给{{1}功能。实际上,它可能会变成:

foo()

但是,具体实现取决于编译器和/或平台。正如Carl Norum指出的那样,如果结构足够小,它甚至可以完全传回寄存器中。

答案 2 :(得分:13)

struct b行不起作用,因为它是语法错误。如果你将它展开以包含它将会正常工作的类型

struct MyObj b = a;  // Runs fine

C在这里做的事实上是从源结构到目的地的memcpy。这对于struct值的赋值和返回(以及C中的所有其他值)都是如此

答案 3 :(得分:9)

是的,我们也可以传递结构和返回结构。你是对的,但你实际上没有传递数据类型,这应该像这个结构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;
}

答案 4 :(得分:4)

可以 在C中分配结构。a = b;是有效的语法。

你只是在你的行中省略了部分类型 - 结构标记 - 不起作用。

答案 5 :(得分:4)

据我所知,C的第一个版本只允许返回一个值 可以放入处理器寄存器,这意味着你只能返回一个指针 结构。同样的限制适用于函数参数。

更新的版本允许传递更大的数据对象,如结构。 我认为这个特征在八十年代或九十年代早期已经很普遍了。

然而,数组仍然可以作为指针传递和返回。

答案 6 :(得分:3)

传回结构没有问题。它将按值传递

但是,如果struct包含任何具有局部变量地址的成员

,该怎么办?
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()返回,name的本地地址就会被释放。 因此,如果我们尝试访问该地址,则在调用者中,它可能会导致分段错误,因为我们正在尝试释放地址。那太糟糕了..

e1.id将完全有效,因为其值将被复制到e2.id

因此,我们应该总是尽量避免返回函数的本地内存地址。

任何malloced都可以在需要时返回

答案 7 :(得分:1)

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一样,名称的内容会被复制到指定的结构变量。

答案 8 :(得分:0)

struct var e2地址作为arg推送到被调用者堆栈,并在那里分配值。实际上,get()在eax reg中返回e2的地址。就像通过引用调用一样。