`
guimingyue
  • 浏览: 75737 次
  • 性别: Icon_minigender_1
  • 来自: 湖北
社区版块
存档分类
最新评论

由链表初始化看C语言的二级指针

阅读更多

先来看C语言创建链表、插入节点和遍历链表的一段代码:

#include <stdio.h>
#include <stdlib.h>

typedef int ElemType;

typedef struct Node{
    ElemType elem;
    struct Node *next;
}Node, *LinkedList;

//void init_linkedlist(LinkedList *list) {
void init_linkedlist(LinkedList *list) {
    *list = (LinkedList)malloc(sizeof(Node));
    (*list)->next = NULL;
}

void insert(LinkedList list, ElemType elem) {
    Node *p, *q;
    q = list;
    p = (Node *)malloc(sizeof(Node));
    p->elem = elem;
    p->next = NULL;
    while(q->next != NULL) q = q->next;
    q->next = p;
}

void main() {
    LinkedList list, p;
    init_linkedlist(&list);
    insert(list, 3);
    insert(list, 4);
    insert(list, 5);
    p = list->next;
    while(p != NULL) {
        printf("%4d", p->elem);
        p = p->next;
    }
    printf("\n");
}

 这个小程序完成的功能很简单,创建一个链表,然后插入3,4,5这三个整数,最后遍历链表输出每个节点中的整数。但是大家注意到没有,在main函数中,初始化链表的函数参数的是一个二级指针,为什么要使用一个二级指针作为参数呢?在任何一本C语言的教材上,都会写C语言的函数参数传递是值传递方式,所有的参数都是通过值传递的,使用指针作为参数可以在函数中改变参数的值(此处感觉表达有误,但是想不到更好的表达方式)。可能有人会有疑问了,在上面代码的main函数中初始化链表的调用函数代码

init_linkedlist(&list);

 list已经是一个指针了,为什么要传递一个指针的地址,直接使用指针不行吗?确实不行。

为什么?那么我们来看看不使用二级指针会发生什么情况,先来看看不使用二级指针的代码:

#include <stdio.h>
#include <stdlib.h>

typedef int ElemType;

typedef struct Node{
    ElemType elem;
    struct Node *next;
}Node, *LinkedList;


void init_linkedlist(Node *list) {
    list = (LinkedList)malloc(sizeof(Node));
    list->next = NULL;
}

void insert(LinkedList list, ElemType elem) {
    Node *p, *q;
    q = list;
    p = (Node *)malloc(sizeof(Node));
    p->elem = elem;
    p->next = NULL;
    while(q->next != NULL) q = q->next;
    q->next = p;
}

void main() {
    LinkedList list, p;
    printf("%d\n", list);
    init_linkedlist(list);
    printf("%d\n", list);
}

 这段代码的main函数中有两处输出,分别是在初始化链表函数之前和之后,如果运行这段代码,会发现两处输出都是一样的值:

 

 但是如果在使用二级指针的代码(本文的第一段代码)中插入相同的两处输出代码,会发现输出的两个值是不同的:



 为什么会这样,原因就在于本文的第一段代码使用的是二级指针作为参数传递,而第二段代码使用的是一级指针作为参数传递。这个问题最终还是回归到了C语言参数传递是值传递了。

如果是使用一级参数传递,首先是main函数中定义一个Node类型的指针,这个指针用list表示,C语言在定义指针的时候也会分配一块内存,一般会占用2个字节或4个字节,现在在大部分的编译器中占用4个字节,这里用4个字节算。在这4个字节的内存中,没有任何值,所以这个指针不指向任何值。然后传递到函数init_linkedlist中,在init_linkedlist函数中,编译器首先为形参list分配一个临时指针内存块,而语句

 

list = (LinkedList)malloc(sizeof(Node));

 中函数malloc分配一块内存,并向该程序返回一个指向这块内存的指针,这样形参list就有值了,在list所表示的临时内存区域中填入刚刚分配的内存的这块内存的地址,假设为0x10086,这样使用(*list)就可以访问这块内存(0x10086)中的内容了。此时在函数init_linkedlist中list所代表的这块内存中的内容是有值的,为0x10086,用图可表示为:

 

但是现在的list只是占据了一个零时的内存空间,这种改变并不能反映到main函数中,init_linkedlist函数执行完了,临时的list内存块就被回收了,这样刚刚分配的内存块的地址0x10086没有被记录下来。而我们如果要初始化main函数中的链表list的话,就必须记录记录下这块内存空间(0x10086)。

     下面我们来考虑使用二级指针的代码,在使用二级指针的代码(本文第一段代码)中,首先定义了一个指针

 

LinkedList list

 然后调用init_linkedlist,调用代码是

 

init_linkedlist(&list);

 这样传递的参数就是一个指针的地址,在函数的参数列表中就表现为指针的指针,函数的定义为

 

void init_linkedlist(LinkedList *L)

 函数的参数是一个二级指针,同样,在执行调用这个函数的时候,临时分配一个指针,这个指针占据一个占用4个字节的内存块(函数执行完要回收的),同时这个临时指针L指向主函数main中定义的list指针,这里假设主函数main中的list指针在内存中的地址为0x12306,用图表示为

 

 其中L是一块临时内存,list是主函数main的中定义的一个指针,此时list代表的内存块中还没有内容(貌似这样说法有误,或者说这块内存还没有初始化)。下面执行内存分配的代码

 

*L = (LinkedList)malloc(sizeof(Node));

 malloc函数分配了一块内存空间,假设地址为0x10010,由于L指向list所代表的内存块,所以*L等价于list,这样将malloc函数分配的内存块赋值给*L就相当于执行语句

 

list = 0x10010

 那么在list代表的内存块中所存储的值就为0x10010,用图表示为

 

 

 这样在函数init_linkedlist中分配的一段内存也就能在main函数中反映出来了,main函数中list代表的内存块的就指向了新分配的内存,链表初始化完成。

 

  • 大小: 26 KB
  • 大小: 16.4 KB
  • 大小: 8.1 KB
  • 大小: 9.1 KB
  • 大小: 13.2 KB
分享到:
评论
3 楼 一袋大米 2014-07-08  
感谢!这是我看了几个回答里最清楚的一个!
2 楼 andy0566 2013-11-20  
看错了,请楼主删回帖
1 楼 andy0566 2013-11-20  
非常不错,不过函数init_linkedlist中的二级指针参数漏了一个*吧
void init_linkedlist(LinkedList [color=red]*[/color]*list) {  
    *list = (LinkedList)malloc(sizeof(Node));  
    (*list)->next = NULL;  
}  

相关推荐

    C语言用指针处理链表

    C语言用指针处理链表 链表是一种重要的数据结构,它可以用来表示复杂的数据结构,并且可以动态地存储分配。链表的特点是每个数据元素在内存中单独分配存储空间,构成一个结点;各数据元素之间通过指针相连,构成一...

    C语言——链表的归并_c语言链表详解

    - 初始化三个链表指针变量`h1`, `h2`, 和`h`。 - 调用`create()`创建第一个链表`h1`,并显示其内容。 - 调用`create()`创建第二个链表`h2`,并显示其内容。 - 调用`merge()`归并`h1`和`h2`,结果存储在`h`中。 ...

    C语言二级真题

    了解如何声明、初始化和操作数组,以及处理字符串(如使用strlen、strcpy、strcat等函数)是必备技能。 5. **结构体与联合** 结构体是C语言中的复合数据类型,可以组合多种不同类型的元素。联合与结构体类似,但其...

    计算机等级考试二级C语言链表复习资料.doc

    1. 创建链表:初始化头节点,并根据需要动态添加节点。 2. 插入节点:在指定位置插入新节点,需要修改前后节点的指针。 3. 删除节点:找到要删除的节点,修改前后节点的指针使其连接。 4. 遍历链表:通过跟随节点的...

    C语言中的指针

    二、指针的初始化 指针在声明时应尽量初始化,避免指向未定义的内存区域。初始化指针的方式通常是让它指向某个已存在的变量,例如: ```c int num = 10; int *p = &num; ``` 在这里,`&num`是取num变量的地址,赋值...

    指针链表文件操作等c语言难点实例程序

    "指针数组和数组指针.txt"这个文件可能包含了如何声明、初始化和使用这两种类型的示例。例如,你可以创建一个指针数组来存储多个字符串的首地址,或者用数组指针遍历二维数组。通过这些实例,你能更好地理解指针在...

    c语言二级上级试题库

    考生应理解指针的声明、初始化、运算以及指针与数组、函数的关系。 3. **数组和字符串**:数组是存储同类型数据的集合,字符串是字符数组的特殊形式。考生需要了解数组的声明、初始化、遍历以及字符串处理函数(如...

    从尾到头打印链表(C语言实现)

    结合这两个函数,我们可以在初始化一个链表后,先调用`reverseList`函数反转链表,然后调用`printListReverse`函数打印链表。这样,就能实现从尾到头的打印。 在实际编程中,还需要考虑一些边界情况,比如链表为空...

    C语言指向指针的指针

    在C语言中,指针是一种存储...最后,需要注意的是,在使用指向指针的指针时,一定要确保对指针进行适当的初始化和检查,避免野指针(即未指向任何有效内存区域的指针)的出现,这样可以防止程序出现不可预测的错误。

    C语言经典但链表,C语言经典但链表,C语言经典但链表

    1. **创建链表**:初始化头节点,并根据需要添加节点。 2. **插入节点**:可以在链表的开头(头部插入)、结尾(尾部插入)或中间插入新节点。 3. **删除节点**:根据节点的值或位置找到并删除节点。 4. **遍历链表*...

    单双向循环链表的转换(C语言)

    - **流程**:读取用户输入的数据`x`,初始化链表,构建单向循环链表,转换为双向循环链表,查找并输出节点`x`的前驱节点数据。 以上就是关于“单双向循环链表的转换(C语言)”的相关知识点总结。通过这些知识点的...

    C语言指针练习填空和阅读程序题

    在使用指针时,需要注意一些问题,例如,指针的初始化、指针的越界访问、指针的赋值等问题。如果不正确地使用指针,可能会导致程序崩溃或出现未定义的行为。 本资源涵盖了C语言指针的基本概念、指针运算、指针与...

    C语言-- 指针经典趣谈

    二、指针的初始化与解引用 在使用指针前,应对其进行初始化,赋予其一个有效的内存地址。例如,我们可以用&运算符获取变量的地址,然后赋值给指针,如int a = 10; int *p = &a;。解引用(*)操作符用于访问指针所...

    C语言链表综合练习题

    其中,创建链表涉及如何初始化头节点;插入和删除元素需要正确处理指针关系;查找元素需要遍历链表;反转链表需要改变相邻节点的指针方向;而合并已排序链表则需要使用到合并排序算法,结合链表特性实现高效合并。 ...

    链表的基本操作 C语言程序

    下面是一个链表初始化的示例代码: ```c LinkStack *Init_LinkStack(){ LinkStack *S; S = (LinkStack*)malloc(sizeof(LinkStack)); S-&gt;next = NULL; return S; } ``` 在上面的代码中,我们首先定义了一个链表...

    链表的19种算法(C语言)

    1. **创建链表**:创建一个空链表,通常涉及到初始化头节点,头节点通常不存储数据,仅用于指向第一个元素。 2. **插入节点**:在链表的特定位置(如开头、末尾或中间)插入新节点。插入操作需要更新前后节点的指针...

    双向链表实现贪吃蛇游戏(C语言版)

    这些函数包括初始化链表,处理蛇的移动,检测碰撞,以及更新游戏状态。比如,`move_snake`函数会根据玩家输入更新蛇的位置,`check_collision`函数则会检查蛇是否撞到自身或边界。 6. **main.c文件** 主程序文件,...

    双链表的应用(C语言)

    2. **初始化链表**:创建一个空的双链表,通常从NULL开始。我们需要一个头节点,它只起到标识链表的作用,不存储实际数据。初始化代码可能如下: ```c Node* head = NULL; ``` 3. **插入节点**:在双链表中插入...

    创建链表模拟堆栈的c语言源程序

    初始化堆栈时,将堆栈顶指针设置为NULL,表示堆栈为空: ```c Stack* createStack() { Stack* stack = (Stack*)malloc(sizeof(Stack)); if (stack == NULL) { // 错误处理 } stack-&gt;top = NULL; stack-&gt;size ...

Global site tag (gtag.js) - Google Analytics