简体   繁体   English

从 C 中的 function 返回一个`struct`

[英]Return a `struct` from a function in C

Today I was teaching a couple of friends how to use C struct s.今天我教几个朋友如何使用 C struct s。 One of them asked if you could return a struct from a function, to which I replied: "No! You'd return pointers to dynamically malloc ed struct s instead."其中一个问你是否可以从 function 返回一个struct ,我回答说:“不!你会返回指向动态malloc ed struct的指针。”

Coming from someone who primarily does C++, I was expecting not be able to return struct s by values.来自主要做 C++ 的人,我期望无法按值返回struct s。 In C++ you can overload the operator = for your objects and makes complete sense to have a function to return your object by value.在 C++ 中,您可以为您的对象重载operator =并且让 function 按值返回您的 object 是完全有意义的。 In C, however, you do not have that option and so it got me thinking what the compiler is actually doing.然而,在 C 中,你没有那个选项,所以我开始思考编译器实际上在做什么。 Consider the following:考虑以下:

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

I understand why struct b = a;我明白为什么struct b = a; should not work -- you cannot overload operator = for your data type.不应该工作——您不能为您的数据类型重载operator = How is it that a = foo(); a = foo();是怎么回事? compiles fine?编译好吗? Does it mean something other than struct b = a;它是否意味着struct b = a;以外的东西? ? Maybe the question to ask is: What exactly does the return statement in conjunction to = sign do?也许要问的问题是: return语句与=符号一起到底做了什么?

You can return a structure from a function (or use the = operator) without any problems.您可以从函数返回结构(或使用=运算符)而不会出现任何问题。 It's a well-defined part of the language.它是语言的一个明确定义的部分。 The only problem with struct b = a is that you didn't provide a complete type. struct b = a的唯一问题是您没有提供完整的类型。 struct MyObj b = a will work just fine. struct MyObj b = a可以正常工作。 You can pass structures to functions as well - a structure is exactly the same as any built-in type for purposes of parameter passing, return values, and assignment.您也可以将结构传递函数 - 出于参数传递、返回值和赋值的目的,结构与任何内置类型完全相同。

Here's a simple demonstration program that does all three - passes a structure as a parameter, returns a structure from a function, and uses structures in assignment statements:这是一个简单的演示程序,它可以完成所有三个操作 - 将结构作为参数传递,从函数返回结构,并在赋值语句中使用结构:

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

The next example is pretty much exactly the same, but uses the built-in int type for demonstration purposes.下一个示例几乎完全相同,但使用内置int类型进行演示。 The two programs have the same behaviour with respect to pass-by-value for parameter passing, assignment, etc.:这两个程序在参数传递、赋值等的按值传递方面具有相同的行为:

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

When making a call such as a = foo();进行诸如a = foo();类的调用时, the compiler might push the address of the result structure on the stack and passes it as a "hidden" pointer to the foo() function. ,编译器可能会将结果结构的地址压入堆栈,并将其作为“隐藏”指针传递给foo()函数。 Effectively, it could become something like:实际上,它可能会变成这样:

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

foo(&a);

However, the exact implementation of this is dependent on the compiler and/or platform.但是,这的确切实现取决于编译器和/或平台。 As Carl Norum notes, if the structure is small enough, it might even be passed back completely in a register.正如 Carl Norum 所指出的,如果结构足够小,它甚至可以在寄存器中完全传回。

The struct b line doesn't work because it's a syntax error. struct b行不起作用,因为它是一个语法错误。 If you expand it out to include the type it will work just fine如果您将其展开以包含类型,它将正常工作

struct MyObj b = a;  // Runs fine

What C is doing here is essentially a memcpy from the source struct to the destination. C 在这里所做的本质上是从源结构到目标的memcpy This is true for both assignment and return of struct values (and really every other value in C)这对于struct值的赋值和返回都是正确的(以及 C 中的所有其他值)

As far as I can remember, the first versions of C only allowed to return a value that could fit into a processor register, which means that you could only return a pointer to a struct.据我所知,C 的第一个版本只允许返回一个可以放入处理器寄存器的值,这意味着您只能返回一个指向结构的指针。 The same restriction applied to function arguments.相同的限制适用于函数参数。

More recent versions allow to pass around larger data objects like structs.更新的版本允许传递更大的数据对象,如结构。 I think this feature was already common during the eighties or early nineties.我认为这个功能在八十年代或九十年代初已经很普遍了。

Arrays, however, can still be passed and returned only as pointers.但是,数组仍然只能作为指针传递和返回。

yes, it is possible we can pass structure and return structure as well.是的,我们也可以传递结构和返回结构。 You were right but you actually did not pass the data type which should be like this struct MyObj b = a.你是对的,但你实际上没有传递应该像这个 struct MyObj b = a 的数据类型。

Actually I also came to know when I was trying to find out a better solution to return more than one values for function without using pointer or global variable.实际上,当我试图找到一种更好的解决方案以在不使用指针或全局变量的情况下为函数返回多个值时,我也开始知道

Now below is the example for the same, which calculate the deviation of a student marks about average.现在下面是相同的例子,它计算学生分数关于平均值的偏差。

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

There is no issue in passing back a struct.传回结构没有问题。 It will be passed by value它将按值传递

But, what if the struct contains any member which has a address of a local variable但是,如果结构包含任何具有局部变量地址的成员呢?

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

Now, here e1.name contains a memory address local to the function get() .现在,这里e1.name包含函数get()本地内存地址。 Once get() returns, the local address for name would have been freed up.一旦get()返回,name 的本地地址就会被释放。 SO, in the caller if we try to access that address, it may cause segmentation fault, as we are trying a freed address.因此,在调用者中,如果我们尝试访问该地址,则可能会导致分段错误,因为我们正在尝试释放地址。 That is bad..那很不好..

Where as the e1.id will be perfectly valid as its value will be copied to e2.id因为e1.id将完全有效,因为它的值将被复制到e2.id

So, we should always try to avoid returning local memory addresses of a function.所以,我们应该总是尽量避免返回函数的本地内存地址。

Anything malloced can be returned as and when wanted任何 malloced 都可以在需要时返回

You can assign structs in C. a = b;可以在 C 中分配结构。 a = b; is valid syntax.是有效的语法。

You simply left off part of the type -- the struct tag -- in your line that doesn't work.您只是在您的行中遗漏了部分类型——结构标记——不起作用。

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

works fine with newer versions of compilers.适用于较新版本的编译器。 Just like id, content of the name gets copied to the assigned structure variable.就像 id 一样,名称的内容被复制到分配的结构变量中。

struct var e2 address pushed as arg to callee stack and values gets assigned there. struct var e2 地址作为 arg 推送到被调用者堆栈,并在那里分配值。 In fact, get() returns e2's address in eax reg.事实上,get() 在 eax reg 中返回 e2 的地址。 This works like call by reference.这就像按引用调用一样。

Worth to mention that this example will print garbage值得一提的是这个例子会打印垃圾

#include <stdio.h>

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

struct emp bad() {
    char name[] = {'J', 'o', 'h', 'n', '\0'};
    struct emp e1 = {404, name};
    return (e1);
}

int main() {
    struct emp e2 = bad();
    printf("%s\n", e2.name);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM