由链表初始化看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代表的内存块的就指向了新分配的内存,链表初始化完成。
由链表初始化看C语言的二级指针的更多相关文章
- C语言复习: 二级指针和多级指针
二级指针内存模型建立 void main2() { int i = 0; //指针数组 char * p1[] = { "123", "456 ...
- 论C语言中二级指针和二维数组之间的区别
刚开始学习C语言的时候,觉得一个数组可以定义一个一级指针去访问,想当然的就觉得可以定义一个二级指针去访问二维数组.很显然这是错误的. 我们来看看C语言的数组在内存中的存储方式. 实际上C语言中的数组, ...
- [编程] C语言的二级指针
用C语言指针作为函数返回值:C语言允许函数的返回值是一个指针(地址),我们将这样的函数称为指针函数函数运行结束后会销毁在它内部定义的所有局部数据 #include<stdio.h> #in ...
- C语言实现二级指针表示字符串数组
头文件: #include<stdlib.h> #include<stdio.h> #include<string.h> 函数原型: char ** createB ...
- C-指针,二级指针,二维数组作为函数参数使用,C语言链表(详解)
一级指针 int *p; //表示定义一个int型(4字节)的指针p &p //表示p自身的地址位置 p ...
- C语言——单链表初始化、求表长、读表元素、插入元素
头文件Linear.h // 单链表的类型定义 typedef struct node { int data; // 数据域 struct node *next; // 指针域 }Node, *Lin ...
- 【转】Linus:利用二级指针删除单向链表
原文作者:陈皓 原文链接:http://coolshell.cn/articles/8990.html 感谢网友full_of_bull投递此文(注:此文最初发表在这个这里,我对原文后半段修改了许多, ...
- 转:Linus:利用二级指针删除单向链表
感谢网友full_of_bull投递此文(注:此文最初发表在这个这里,我对原文后半段修改了许多,并加入了插图) Linus大婶在slashdot上回答一些编程爱好者的提问,其中一个人问他什么样的代码是 ...
- 逆置单链表(基于c语言)
直接插入全部代码:(reverseLinklist函数是逆置操作) #include <stdio.h> #include <stdlib.h> #include <as ...
随机推荐
- 自定义Operation
1.要自定义一个Operation 首先要创建一个继承于NSOperation的类. 2.在创建好的类的.h文件声明自定义的方法:-(instancetype)initWithDownLoadMess ...
- 类库探源——System.Environment
Environment 类: 提供有关当前环境和平台的信息以及操作它们的方法.此类不能被继承. 命名空间: System 程序集: mscorlib.dll 继承关系: 常用属性(字段)和方法: ...
- SVM技法
PLA不管胖瘦,SVM喜欢胖的 fewer dichotomies=> small VC 演算法的VC dimension shatter 掉3个点 如果限制胖瘦,两个点都shatter不掉 喜 ...
- 翻纸牌 高校俱乐部 英雄会 csdn
题目描述 有一种纸牌游戏,很有意思,给你N张纸牌,一字排开,纸牌有正反两面,开始的纸牌可能是一种乱的状态(有些朝正,有些朝反),现在你需要整理这些纸牌.但是麻烦的是,每当你翻一张纸牌(由正翻到反,或者 ...
- (三)跟我一起玩Linux网络服务:DHCP服务配置之主服务器配置
我们今天来做DHCP服务器的配置,我们的前提示要实现用一台虚拟机做DHCP服务器 1.首先,我们要有DHCP软件,我们用到下面两个软件(可以使用其他方法从网上直接安装,具体方法网络搜索) dhcp-3 ...
- apache 网址重定向
参考了以下网站,终于基本搞定b2c网站伪静态.剩下的就是体力活了. 回家后整理下. http://yp.oss.org.cn/software/show_resource.php?resource_i ...
- 查看yum包安装地址
首先找到包含版本号在内的全包名 rpm -qa|grep t_dp_apsara_exstoret_dp_apsara_exstore-1.0.5-56 然后就可以查询到了 rpm -ql t_dp_ ...
- 【转】转移Package Cache文件夹,转移Windows Installer文件夹
详见http://blogs.msdn.com/b/heaths/archive/2014/02/11/how-to-relocate-the-package-cache.aspx (注意:若Wind ...
- JSP中使用的模式——JSP+JavaBean
模式二:JSP+Servlet+JavaBean 链接地址:http://wxmimperio.coding.io/?p=189 JSP中两种模式的总结 链接地址:http://wxmimperio. ...
- 从零开始学习MySQL3---数据库的基本操作
创建数据库 MySQL安装完成后,将会在其Data目录下自动创建几个必需的数据库 可以用 SHOW DATABASES: 来查看当前存在的数据库 创建数据库是在系统磁盘上划分一块区域用于数据的存储和 ...