1 #include<stdio.h> 2 #include<stdlib.h> 3 4 struct list{ 5 int a; 6 list *next; 7 }; 8 9 list * node() 10 { 11 return (list *)malloc(sizeof(list)); 12 } 13 14 int main() 15 { 16 17 list *l = node(); 18 19 return 0; 20 }
1 #include<stdio.h> 2 #include<stdlib.h> 3 4 struct list{ 5 int a; 6 struct list *next; 7 }; 8 9 struct list * node() 10 { 11 return (struct list *)malloc(sizeof(struct list)); 12 } 13 14 int main() 15 { 16 17 struct list *l = node(); 18 19 return 0; 20 }
1 单链表带头结点&不带头结点 2 3 Node *head; //声明头结点 4 5 带头结点初始化 6 void InitList(Node **head){ 7 8 *head=(Node *)malloc( sizeof(Node)); 9 (*head)->next=NULL; 10 } 11 12 带头结点尾插入,统一操作 13 方式一: 14 void CreatList(Node **head){ 15 Node *r=*head,*s; 16 int a; 17 while(scanf("%d",&a)){ 18 if(a!=0){ 19 s=(Node *)malloc(sizeof(Node)); 20 s->value=a; 21 r->next=s; 22 r=s; 23 } 24 else{ 25 r->next=NULL; 26 break; 27 } 28 } 29 } 30 调用CreatList(&head); 31 32 方式二: 33 void CreatList(Node *head){ 34 Node *r=head,*s; 35 ... //下面的都一样 36 } 37 调用CreatList(head); 38 39 ------------------------------------------------------------------------------------------------------ 40 41 不带头结点初始化 42 方式一: 43 void InitList(Node **head){ 44 *head=NULL; 45 } 46 调用InitList(&head); 47 48 方式二: 49 void InitList(Node *head){ 50 head=NULL; 51 } 52 调用InitList(head); 53 54 不带头结点尾插入,第一个节点与其他节点分开操作 55 void CreatList(Node **head){ 56 Node *p,*t; /*p工作指针,t临时指针*/ 57 int a,i=1; 58 while(scanf("%d",&a)){ 59 if(a!=0){ 60 t=(Node *)malloc(sizeof(Node)); 61 t->value=a; 62 if(i==1){ 63 *head=t; 64 } 65 else{ 66 p->next=t; 67 } 68 p=t; 69 } 70 else{ 71 p->next=NULL; 72 break; 73 } 74 i++; 75 } 76 } 77 调用CreatList(&head); 78 79 80 一、两者区别: 81 1、不带头结点的单链表对于第一个节点的操作与其他节点不一样,需要特殊处理,这增加了程序的复杂性和出现bug的机会,因此,通常 82 在单链表的开始结点之前附设一个头结点。 83 2、带头结点的单链表,初始时一定返回的是指向头结点的地址,所以一定要用二维指针,否则将导致内存访问失败或异常。 84 3、带头结点与不带头结点初始化、插入、删除、输出操作都不样,在遍历输出链表数据时,带头结点的判断条件是while(head->next!=NULL), 85 而不带头结点是while(head!=NULL),虽然头指针可以在初始时设定,但是如1所述,对于特殊情况如只有一个节点会出现问题。 86 87 88 二、为什么不带头结点初始化有2种方式,而带头结点只有1种方式呢? 89 90 因为不带头结点声明Node *head 时;C编译器将其自动初始化为NULL,于是根本不需要调用InitList(head);也即不带头结点的初始化 91 是个伪操作。而带头结点的初始化在堆开辟了一段内存,需要修改head指针变量指向的地址(即head的值),所以要修改head的值,必须传保 92 存head变量的地址(即二维指针)。而直接调用CreatList(head);相当于传head变量的值,函数修改的是head的副本,无法真正改变head的值。 93 94 注:这里可以将head指针看成一个变量(不管它保存的是地址),就比较好理解了。 95 96 97 三(key)、其实本质上还是传值,传址的问题,只不过指针本身保存的地址,让这个过程变得有点纠结。在函数调用需要修改指针变量的指向(value)时, 98 应该传递指针变量的地址(address)。 99 另外,对于函数的形参是指针时,只要该参数不在左边(即都是右值操作),二维指针(形参)就可以简化为一维指针。如上面带头结点的尾插 100 简化版本。