[英]Pointer to pointer in linked list
有人可以解釋一下為什么這段代碼給我的結果是空列表:
typedef struct str_node{
int data;
struct str_node *next;
}node;
void begin(node *head);
void display_list(node *head);
int main(){
node *head;
int i;
head = NULL;
for(i=0;i<5;i++) {
begin(head);
}
display_list(head);
return 0;
}
void begin(node *head){
node *new;
int value;
new = (node*) malloc(sizeof(node));
printf("Insert the element to add at the beginning of the list: ");
scanf("%d",&value);
new->data = value;
new->next = head;
head = new;
}
但是,如果我用指向指針的指針更改 begin() function 它會給我正確的列表嗎?
void begin(node **head){
node *new;
int value;
new = (node*) malloc(sizeof(node));
printf("Insert the element to add at the beginning of the list: ");
scanf("%d",&value);
new->data = value;
new->next = *head;
*head = new;
}
你能解釋一下為什么當我將節點頭傳遞給 function 開始時,我必須將它作為“&head”傳遞嗎? 不再是“頭”
在此代碼片段的第一個程序中
head = NULL;
for(i=0;i<5;i++) {
begin(head);
}
指針head
通過值傳遞給begin
。 即main中聲明的指針head
的值的副本被創建並分配給與function開頭的同名參數
void begin(node *head);
因此,在 function 中,參數head
最初保存了更改的原始指針head
的副本。 其值分配給參數的原始指針head
沒有被更改。
要更改 main 中聲明的原始指針頭,您必須通過指向指針頭的指針間接引用將其傳遞給 function,就像在第二個程序中所做的那樣。
所以 function 應該聲明為
void begin(node **head);
而且您必須通過指向它的指針間接傳遞指針頭
begin( &head );
在這種情況下,取消引用傳遞的指針 function 將直接訪問 main 中聲明的原始指針頭並可以更改它(不是其值的副本,因為它發生在第一個 function 定義中)
new->next = *head;
*head = new;
為了更清楚,考慮這個簡單的演示程序。
#include <stdio.h>
typedef int T;
void f( T t )
{
t = 10;
}
int main(void)
{
T t = 0;
printf( "Before calling f t is equal to %d\n", t );
f( t );
printf( "After calling f t is equal to %d\n", t );
return 0;
}
它的 output 是
Before calling f t is equal to 0
After calling f t is equal to 0
由於 function f 處理傳遞參數值的副本,因此 main 中聲明的變量t
的值沒有改變。
所以你需要通過指針通過引用傳遞原始變量t
#include <stdio.h>
typedef int T;
void f( T *t )
{
*t = 10;
}
int main(void)
{
T t = 0;
printf( "Before calling f t is equal to %d\n", t );
f( &t );
printf( "After calling f t is equal to %d\n", t );
return 0;
}
現在程序 output 是
Before calling f t is equal to 0
After calling f t is equal to 10
在這些演示程序中,名稱T
用作int
類型的別名,而 object t
主要具有這種類型。
現在讓我們假設名稱 T 是類型 int * 的別名。
typedef int * T;
在這種情況下, main 中的聲明例如
T t = NULL;
表示變量t
具有指針類型int *
。 也就是說它相當於
int * t = NULL;
因此,要將其傳遞給必須更改原始變量 t 的 function,我們需要通過引用傳遞它,例如
f( &t );
這意味着相應的 function 的參數類型應聲明為
void f( T *t );
但由於T
是int *
的別名,因此這意味着 function 具有int **
類型的參數。
void f( int * *t );
因為head
(實際上)是一個局部變量,所以更改它在 function 之外沒有任何影響,而更改*head
會更改head
指向的內容,因此會更改。
如果您希望 function 能夠更改int
變量(例如x
)中的值,則可以將指向x
的指針傳遞給它,該指針的類型為int*
,您將使用&x
獲得指向x
的指針。 無論x
是什么類型,都一樣。
聲明可能會引起一些混亂
node *head;
代替
node* head;
您正在聲明head
。 head
是變量,它是一個指針。 它不是一個節點。 另請注意,節點不是鏈表:鏈表是節點的集合,可能還有其他東西,以便有一個有用的實現。 更多關於這個稍后在最后。
事實是您在main()
中聲明了head
,只是一個node*
。 節點本身甚至還不存在。 您將begin()
聲明為
void begin(node *head);
我想你會更清楚地看到它
void begin(node* parameter);
parameter
是node*
。
在begin()
中,您會獲得指針的副本,並且更改指針不會更改main()
中的原始指針。 在您的情況下,它將在main()
中永遠指向NULL
。
重要的是指針就像任何變量:指針有一個地址。 和一個內容。 當您按值傳遞時,就像您所做的那樣, begin()
中的指針以NULL
,即來自main()
的 VALUE。 但是它們之間的聯系在調用中結束:初始值。
當您將指針傳遞給begin()
時,使用運算符 'address of' 並編寫&head
事情會發生變化:您將使用運算符'*'
更改它,這意味着您將更改它指向的地址,因此它將在main()
。 由於head
是node*
指向它的指針將被聲明為node**
但是考慮使用以下方法更改鏈表的begin()
聲明:
node* begin(node* node);
邏輯是插入一個節點可以改變鏈表的頭部,所以你返回新地址,如
node* _insert_begin(int value, node* pNode)
{
node* new = (node*)malloc(sizeof(node));
new->data = value;
new->next = pNode;
return new;
}
是一種常用的寫法。 另一種是使用node**
。
我在這里描述的方式,任何可以改變列表頭部的操作都必須
node* _insert_begin(int value, node* pNode)
{ // insert 'value' at the start of the list
node* new = (node*)malloc(sizeof(node));
(*new).data = value;
new->next = pNode;
return new;
}
返回new
head
得到更新。 你可以寫在main()
node* another = NULL;
display_list(another);
// inserts 5 to 0 at the beginning
for (int i = 5; i >= 0; i -= 1)
another = _insert_begin(i, another);
printf("inserted 5..0 at the beginning\n");
display_list(another);
注意another = _insert_begin(i, another);
你會看到main()
中的指針是如何更新的。
empty list
inserted 5..0 at the beginning
0 1 2 3 4
5
list has 6 elements
使用display_list()
的這個實現,每行打印 5 個值:
int display_list(node* p)
{
if (p == NULL)
{
printf("empty list\n");
return 0;
};
int count = 0;
// not empty
do
{
printf("%8d ", p->data);
count++;
if (count % 5 == 0) printf("\n");
p = p->next;
} while (p != NULL);
if (count % 5 != 0) printf("\n");
printf("list has %d elements\n", count);
return count;
};
注意最后插入也可以改變頭部,在列表為空的情況下,所以我們仍然需要返回頭部地址
node* _insert_end(int value, node* pNode)
{ // insert value at the end of the list
node* new = (node*)malloc(sizeof(node));
new->data = value;
new->next = NULL;
if (pNode == NULL) return new;
node* p = pNode;
while (p->next != NULL) p = p->next;
p->next = new;
return pNode;
}
當然,以升序插入也可以改變頭部,如
node* _insert_ordered(int value, node* pNode)
{ // insert value at ascending order in the list
node* new = (node*)malloc(sizeof(node));
new->data = value;
new->next = NULL;
if (pNode == NULL) return new;
node* p = pNode;
node* prev = NULL; // previous node: list if forward only
while (p->next != NULL)
{
if (new->data < p->data)
{
// insert before first greater than value
if (prev == NULL)
{
// new head
new->next = p;
return new;
}; // if()
prev->next = new;
new->next = p;
return pNode; // no change in head
};
prev = p; p = p->next; // updates pointers
}; // while()
// we are at the end: new will be the last?
if (new->data < p->data)
{
if (prev == NULL)
pNode = new;
else
prev->next = new;
new->next = p;
}
else
{
p->next = new;
};
return pNode;
} // _insert_ordered()
刪除列表還應該返回一個node*
以使頭指針無效。 這是正常的。 當您習慣了它的機制時,這可以確保不會留下無效的指針。
請注意,此邏輯是協作的:您必須在每次可以更改頭部的調用時重新分配頭部指針
node* delete_list(node* H)
{
if (H == NULL) return NULL;
if (H->next == NULL)
{ // single node
free(H);
return NULL;
};
// more than one node
do
{ node* p = H->next;
free(H);
H = p;
} while (H != NULL);
return NULL;
};
empty list
inserted 5..0 at the beginning
0 1 2 3 4
5
list has 6 elements
inserted 6 to 10 at the end
0 1 2 3 4
5 6 7 8 9
10
list has 11 elements
inserted 0 to 10, ordered
0 0 1 1 2
2 3 3 4 4
5 5 6 6 7
7 8 8 9 9
10 10
list has 22 elements
inserted -1 to -10, ordered
-10 -9 -8 -7 -6
-5 -4 -3 -2 -1
0 0 1 1 2
2 3 3 4 4
5 5 6 6 7
7 8 8 9 9
10 10
list has 32 elements
inserted 11 to 20, ordered
-10 -9 -8 -7 -6
-5 -4 -3 -2 -1
0 0 1 1 2
2 3 3 4 4
5 5 6 6 7
7 8 8 9 9
10 10 11 12 13
14 15 16 17 18
19 20
list has 42 elements
about to delete list
empty list
#include <stdio.h>
#include <stdlib.h>
typedef struct str_node
{
int data;
struct str_node* next;
} node;
void begin(node* pNode);
node* delete_list(node*);
int display_list(node*);
node* _insert_begin(int, node*);
node* _insert_end(int, node*);
node* _insert_ordered(int, node*);
int main()
{
node* another = NULL;
display_list(another);
// insert 5 to 0 at the beginning
for (int i = 5; i >= 0; i -= 1)
another = _insert_begin(i, another);
printf("inserted 5..0 at the beginning\n");
display_list(another);
// insert 6 to 10 at the end
for (int i = 6; i <= 10; i += 1)
another = _insert_end(i, another);
printf("inserted 6 to 10 at the end\n");
display_list(another);
// insert 0 to 10 ordered
for (int i = 0; i <=10; i += 1)
another = _insert_ordered(i, another);
printf("inserted 0 to 10, ordered\n");
display_list(another);
// insert -1 to -10 ordered
for (int i = -1; i >= -10; i -= 1)
another = _insert_ordered(i, another);
printf("inserted -1 to -10, ordered\n");
display_list(another);
// insert 11 to 20 ordered
for (int i = 11; i <= 20; i += 1)
another = _insert_ordered(i, another);
printf("inserted 11 to 20, ordered\n");
display_list(another);
printf("about to delete list\n");
another = delete_list(another);
display_list(another);
return 0;
}
node* delete_list(node* H)
{
if (H == NULL) return NULL;
if (H->next == NULL)
{ // single node
free(H);
return NULL;
};
// more than one node
do
{ node* p = H->next;
free(H);
H = p;
} while (H != NULL);
return NULL;
};
node* _insert_begin(int value, node* pNode)
{ // insert 'value' at the start of the list
node* new = (node*)malloc(sizeof(node));
(*new).data = value;
new->next = pNode;
return new;
}
node* _insert_end(int value, node* pNode)
{ // insert value at the end of the list
node* new = (node*)malloc(sizeof(node));
new->data = value;
new->next = NULL;
if (pNode == NULL) return new;
node* p = pNode;
while (p->next != NULL) p = p->next;
p->next = new;
return pNode;
}
node* _insert_ordered(int value, node* pNode)
{ // insert value at ascending order in the list
node* new = (node*)malloc(sizeof(node));
new->data = value;
new->next = NULL;
if (pNode == NULL) return new;
node* p = pNode;
node* prev = NULL; // previous node: list if forward only
while (p->next != NULL)
{
if (new->data < p->data)
{
// insert before first greater than value
if (prev == NULL)
{
// new head
new->next = p;
return new;
}; // if()
prev->next = new;
new->next = p;
return pNode; // no change in head
};
prev = p; p = p->next; // updates pointers
}; // while()
// we are at the end: new will be the last?
if (new->data < p->data)
{
if (prev == NULL)
pNode = new;
else
prev->next = new;
new->next = p;
}
else
{
p->next = new;
};
return pNode;
} // _insert_ordered()
int display_list(node* p)
{
if (p == NULL)
{
printf("empty list\n");
return 0;
};
int count = 0;
// not empty
do
{
printf("%8d ", p->data);
count++;
if (count % 5 == 0) printf("\n");
p = p->next;
} while (p != NULL);
if (count % 5 != 0) printf("\n");
printf("list has %d elements\n", count);
return count;
};
考慮以下
struct no
{
void* item;
struct no* next;
struct no* prev;
}; // no
typedef struct no Node;
typedef struct
{ // example, more flexible
char* name;
unsigned size;
unsigned capacity;
Node* head;
Node* tail;
} Linked_list;
這樣,鏈表就被定義為節點的容器。
name
。size
始終可用並且是最新的capacity
來實現void*
的指針 Linked_list ll_one;
Linked_list many_ll[20];
Linked_list* pLL = &ll_one;
關於:
void begin(node *head){
更改head
只會更改調用堆棧的“head”,需要更改調用者的 function 中的“head”指向的位置。 為此,調用者必須傳遞“head”的地址。 'head' 本身就是一個指針這一事實無助於明確需要做什么,
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.