[英]Is it valid to pass a compound literal into a function?
我有一個結構類型作為參數,需要將它傳遞給函數。
整個代碼如下:
void insert(struct node *newt) {
struct node *node = head, *prev = NULL;
while (node != NULL && node->data < newt->data) {
prev = node;
node = node->next;
}
newt->next = node;
if (prev == NULL)
head = newt;
else
prev->next = newt;
}
void print(){
struct node *tmp = head;
while(tmp){
printf("%d->",tmp->data);
tmp = tmp->next;
}
printf("\n");
}
int main(){
/*
* Experiment for naive-insert
*/
for(int i = 1; i < 4; i++){
insert(&(struct node){.data = i, .next = NULL});
}
//insert(&(struct node){.data = 1, .next = NULL});
//insert(&(struct node){.data = 2, .next = NULL});
//insert(&(struct node){.data = 3, .next = NULL});
print();
}
如果我在 for 循環中調用 insert ,它會打印 .....->3->3->3....(無法停止)
但是如果我只是將 for 循環替換為
insert(&(struct node){.data = 1, .next = NULL});
insert(&(struct node){.data = 2, .next = NULL});
insert(&(struct node){.data = 3, .next = NULL});
它會正常運行。 我想知道我的 for 循環版本代碼發生了什么。
[編輯:在最初的草案中,我對用復合文字創建的存儲的性質是錯誤的。 Jonathan Leffler 的評論和 Eric Postpischil 的回答是正確的,並證明了我最初的觀點是錯誤的。 我建議的另一種創建鏈接列表的方法是有效的,不再需要 mut。 我已經修改了我的答案。]]
循環中的代碼等效於:
for (int i = 0; i < 3; i++) {
struct node tmp = {.data = i, .next = NULL};
insert(&tmp);
}
編譯器創建一個臨時對象並將其地址傳遞給您的列表函數。 該臨時對象僅在循環體中有效,並在插入后直接超出范圍。 在打印列表時通過列表中的指針訪問循環后無效的對象是未定義的行為。)
沒有循環的代碼等價於:
struct node tmp1 = {.data = 1, .next = NULL};
struct node tmp2 = {.data = 2, .next = NULL};
struct node tmp3 = {.data = 3, .next = NULL};
insert(&tmp1);
insert(&tmp2);
insert(&tmp3);
只要tmpX
變量不超出范圍,您的鏈表就很好。
您正在嘗試使用臨時值初始化鏈表中的新節點 - 這不會很好地結束!!
for(int i = 0; i < 3; i++){
insert(&(struct node){.data = i, .next = NULL});
}
這里顯然發生的是編譯器回收了堆棧上的相同內存來生成臨時變量。 實際上,前一個節點與下一個節點相同,因此您最終會得到指向自身的相同節點(其最后一個值為 2)。
您可以通過在插入開頭添加以下行來查看這一點:
printf("%p\n", newt);
你真的應該使用 malloc 而不是這樣做。
C 2018 6.5.2.5 5 說函數內的復合文字具有自動存儲持續時間:
… 如果復合字面量出現在函數體之外,則對象具有靜態存儲期; 否則,它具有與封閉塊相關聯的自動存儲持續時間。
這意味着從程序執行到達其聲明的時間到封閉塊的執行結束,存儲是為復合文字保留的。
編寫代碼時:
int main(void)
{
…
insert(&(struct node){.data = 1, .next = NULL});
insert(&(struct node){.data = 2, .next = NULL});
insert(&(struct node){.data = 3, .next = NULL});
print();
}
那么封閉塊是形成main
主體的{ … }
,並且復合文字一直存在,直到main
執行結束。 所以它們仍然存在(意味着在執行print
為它們保留了存儲空間)。
在這段代碼中:
for(int i = 1; i < 4; i++){
insert(&(struct node){.data = i, .next = NULL});
}
封閉塊是{ … }
,它是 for 循環的主體。 每次for
循環迭代時,該塊的執行就會結束。 因此,當稍后調用print
例程時,這些復合文字不再存在(存儲不是為它們中的任何一個保留的,並且很可能已連續重用於它們中的每一個)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.