簡體   English   中英

從 C 中的 function 返回一個`struct`

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

今天我教幾個朋友如何使用 C struct s。 其中一個問你是否可以從 function 返回一個struct ,我回答說:“不!你會返回指向動態malloc ed struct的指針。”

來自主要做 C++ 的人,我期望無法按值返回struct s。 在 C++ 中,您可以為您的對象重載operator =並且讓 function 按值返回您的 object 是完全有意義的。 然而,在 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可以正常工作。 您也可以將結構傳遞函數 - 出於參數傳遞、返回值和賦值的目的,結構與任何內置類型完全相同。

這是一個簡單的演示程序,它可以完成所有三個操作 - 將結構作為參數傳遞,從函數返回結構,並在賦值語句中使用結構:

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

進行諸如a = foo();類的調用時 ,編譯器可能會將結果結構的地址壓入堆棧,並將其作為“隱藏”指針傳遞給foo()函數。 實際上,它可能會變成這樣:

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

foo(&a);

但是,這的確切實現取決於編譯器和/或平台。 正如 Carl Norum 所指出的,如果結構足夠小,它甚至可以在寄存器中完全傳回。

struct b行不起作用,因為它是一個語法錯誤。 如果您將其展開以包含類型,它將正常工作

struct MyObj b = a;  // Runs fine

C 在這里所做的本質上是從源結構到目標的memcpy 這對於struct值的賦值和返回都是正確的(以及 C 中的所有其他值)

據我所知,C 的第一個版本只允許返回一個可以放入處理器寄存器的值,這意味着您只能返回一個指向結構的指針。 相同的限制適用於函數參數。

更新的版本允許傳遞更大的數據對象,如結構。 我認為這個功能在八十年代或九十年代初已經很普遍了。

但是,數組仍然只能作為指針傳遞和返回。

是的,我們也可以傳遞結構和返回結構。 你是對的,但你實際上沒有傳遞應該像這個 struct 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;
}

傳回結構沒有問題。 它將按值傳遞

但是,如果結構包含任何具有局部變量地址的成員呢?

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 都可以在需要時返回

可以在 C 中分配結構。 a = b; 是有效的語法。

您只是在您的行中遺漏了部分類型——結構標記——不起作用。

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 一樣,名稱的內容被復制到分配的結構變量中。

struct var e2 地址作為 arg 推送到被調用者堆棧,並在那里分配值。 事實上,get() 在 eax reg 中返回 e2 的地址。 這就像按引用調用一樣。

值得一提的是這個例子會打印垃圾

#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