簡體   English   中英

Function 返回 C 中的指針

[英]Function return a pointer in C

我是 C 的新手,我嘗試創建一個 function 返回一個指針。 我使用了不同的方法來做到這一點:

1.

typedef struct student{
    int age;
}student;

student *create_student(){
    student* s;
    s-> age= 24;
    return s;
}

int main() {
    student* s = create_student();
    printf("the student age is %d", s->age);
    return 0;
}

它編譯但似乎不起作用。

2.

typedef struct student{
    int age;
}student;

student *create_student(){
    student* s;
    student s2;
    s2.age = 24;
    s = &s2;

    return s;
}

int main() {
    student* s = create_student();
    printf("the student age is %d", s->age);
    return 0;
}

它似乎有效,並打印“學生年齡為 24 歲”,但如果我在之前的 printf 之前添加了一條 printf 語句:

int main() {
    student* s = create_student();
    printf("This is a test\n");
    printf("the student age is %d", s->age);
    return 0;
}

它給了我:

這是一個測驗

學生年齡是-1422892954

3.

如果我使用以下方式:

typedef struct student{
    int age;
}student;

student *create_student(){
    student* s = malloc(sizeof(student));
    s -> age = 24;
    return s;
}

int main() {
    student* s = create_student();
    // printf("This is a test\n");
    printf("the student age is %d", s->age);
    return 0;
}

它適用於兩種情況,有和沒有注釋的 printf

我只想知道它對 1 和 2 失敗的原因是什么。為什么它對 3 有效? 一般來說,我們什么時候應該使用 malloc 什么時候不應該使用?

謝謝

示例 1

您的示例 1 不起作用,因為沒有創建student object。

student* s;

這將創建一個指針s ,它應該指向一個student ,但當前指向一個未知的 memory 位置,因為它是一個未初始化的變量。 它絕對不是指向一個新學生,因為到目前為止還沒有創建。

s->age = 24;

然后,這將寫入s當前指向的未知 memory 位置,在此過程中破壞 memory。 您現在輸入了未定義行為 (UB)的 realm 。 您的進程可能會在此時或稍后崩潰,或者它可能會做一些瘋狂和意想不到的事情。

考慮在此之后會發生什么是沒有意義的,因為您的進程現在已經注定了,需要終止。

示例 2

您的示例 2 可以工作,但僅在某些時候有效,因為 UB 再次發揮作用。

student s2;

在這里,您正在創建一個student作為局部變量。 它可能是在堆棧上創建的。 在您離開 function create_student之前,該變量一直有效。

但是,您將創建一個指向該student object 的指針,並從您的 function 返回它 這意味着,外部代碼現在有一個指向student object 曾經所在的位置的指針,但是由於您從 function 返回並且它是一個局部變量,它不再存在,排序。 那是。 這是一個僵屍,或者,更好的解釋是,就像您刪除硬盤上的一個文件 - 只要沒有其他文件覆蓋它在磁盤上的位置。 您仍然可以恢復它,因此,幸運的是,即使在create_student返回之后,您也可以讀取它的age 但是,一旦您稍微更改場景(通過插入另一個printf ),您就會運氣不佳,並且printf調用使用其自己的局部變量,這些變量會覆蓋堆棧上的student object。 哎呀。 這是因為使用指向不再存在的 object 的指針也是未定義行為 (UB)。

示例 3

這個例子有效。 它是穩定的,它沒有未定義的行為(幾乎 - 見下文)。 那是因為您使用malloc而不是堆棧上創建student 這意味着它現在永遠存在(或者直到你free調用它),並且當你的 function 返回時不會被丟棄。 因此,傳遞一個指向它的指針並從另一個地方訪問它是有效的。

只是一個小問題——如果malloc失敗了怎么辦,例如你用完了 memory? 在那種情況下,我們又遇到了問題。 因此,您應該檢查malloc是否返回NULL並以某種方式處理錯誤。 否則, s->age = 24將嘗試取消引用 null 指針,該指針再次不起作用。

但是,您還應該記住在使用完畢后將其free ,否則您會出現 memory 泄漏。 在你這樣做之后,請記住現在你的指針確實變得無效並且你不能再使用它,否則我們又回到了 UB 世界。

至於您何時使用malloc的問題:基本上每當您需要在離開當前 scope 后創建可以繼續存在的東西時,或者當某些東西是本地的但必須很大(因為堆棧空間有限)時,或者當某些東西必須大小可變(因為您可以將所需的大小作為參數傳遞給malloc )。

最后要注意的一件事:您的示例 3 有效,因為您的student只有一個字段age ,並且您在再次閱讀之前將該字段初始化為24 這意味着所有字段(因為它只有一個)都已初始化。 如果它有另一個字段(例如name )並且您沒有初始化那個字段,如果您在其他地方的代碼試圖讀取那個未初始化的name ,您仍然會攜帶一個“UB 定時炸彈”。 因此,請始終確保所有字段都已初始化。 您還可以使用calloc而不是malloc來讓 memory 在傳遞給您之前用零填充,然后您可以確定您有一個可預測的 state 並且它不再未定義。

因為在 case1 和 2 中,變量“age”在子函數 create_student() 的 scope 中,在堆棧上。 所以一旦子功能完成,那個區域就被釋放了,也就意味着“年齡”也被釋放了。 所以 s 現在指向一個無意義的區域。 如果你足夠幸運,該區域仍然存儲“年齡”,你可以打印出該信息,這就是它在 case2 的第一部分工作的原因。

但在 case3 中,student* s 指向一個堆區域,當該子功能完成時,該堆區域將不再空閑。 所以 s->age 仍然有效。

繼續評論,你可以這樣想:

情況1。

    student* s;

s在未初始化的指針中,它不指向您可以有效使用的任何 memory。 它的價值是不確定的。

案例 2。

    student* s;
    student s2;
    s2.age = 24;
    s = &s2;

s是一個未初始化的指針(如 1.), s2聲明一個有效的結構並且s2.age被有效地初始化。 s = &s2; s2的地址(聲明為函數的本地)分配給s 返回s時, s2無效——它的壽命僅限於 function,因此返回到main()的地址無效。

案例 3。

    student* s = malloc(sizeof(student));

是的!! s現在保存 memory 的有效塊的起始地址,該塊具有分配的存儲持續時間(有利於程序的生命周期或直到被釋放)。 唯一的問題是您需要驗證分配是否成功:

    if (s == NULL) {
        perror ("malloc-s");
        return NULL;
    }

現在您可以確信返回是有效地址或NULL指示分配失敗。

何時使用

對於最后一個問題,當您不知道需要多少東西時,您會動態分配,因此您聲明其中的一些,跟蹤您使用了多少,並在最初分配的塊被填充時realloc更多。 或者,您需要比程序堆棧更多的東西,您可以分配或聲明為static (或全局聲明)。 否則,如果您事先知道需要多少,並且可以放入堆棧,只需聲明它們的數組即可。

如果您還有其他問題,請告訴我。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM