[英]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 不起作用,因為沒有創建student
object。
student* s;
這將創建一個指針s
,它應該指向一個student
,但當前指向一個未知的 memory 位置,因為它是一個未初始化的變量。 它絕對不是指向一個新學生,因為到目前為止還沒有創建。
s->age = 24;
然后,這將寫入s
當前指向的未知 memory 位置,在此過程中破壞 memory。 您現在輸入了未定義行為 (UB)的 realm 。 您的進程可能會在此時或稍后崩潰,或者它可能會做一些瘋狂和意想不到的事情。
考慮在此之后會發生什么是沒有意義的,因為您的進程現在已經注定了,需要終止。
您的示例 2 可以工作,但僅在某些時候有效,因為 UB 再次發揮作用。
student s2;
在這里,您正在創建一個student
作為局部變量。 它可能是在堆棧上創建的。 在您離開 function create_student
之前,該變量一直有效。
但是,您將創建一個指向該student
object 的指針,並從您的 function 返回它。 這意味着,外部代碼現在有一個指向student
object 曾經所在的位置的指針,但是由於您從 function 返回並且它是一個局部變量,它不再存在,排序。 那是。 這是一個僵屍,或者,更好的解釋是,就像您刪除硬盤上的一個文件 - 只要沒有其他文件覆蓋它在磁盤上的位置。 您仍然可以恢復它,因此,幸運的是,即使在create_student
返回之后,您也可以讀取它的age
。 但是,一旦您稍微更改場景(通過插入另一個printf
),您就會運氣不佳,並且printf
調用使用其自己的局部變量,這些變量會覆蓋堆棧上的student
object。 哎呀。 這是因為使用指向不再存在的 object 的指針也是未定義行為 (UB)。
這個例子有效。 它是穩定的,它沒有未定義的行為(幾乎 - 見下文)。 那是因為您使用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.