带头结点的链表初始化 和不带头结点的链表初始化

我一般常用的不带头结点的链表初始化:

#include <iostream>
using namespace std;

typedef struct node {
	int data;
	struct node* next;
}lnode, *list;

list createList()
{
	int data = 0;
	cout<<"please Enter Data: (end of 0)"<<endl;
	cin>>data;

	node *p = NULL,*q;
	list l = p;
	
	if( 0 != data ){
		p = new node;
		p->data = data;
		p->next = NULL;
	}else {
		return NULL;
	}
	
	l = p;
	cout<<"please Enter Data: (end of 0)"<<endl;
	cin>>data;
	while ( 0 != data ){
		q = new node;
		q->data = data;
		q->next = NULL;

		p->next = q;
		p = q;

		cout<<"please Enter Data: (end of 0)"<<endl;
		cin>>data;
	}
	return l;
}

void printList(list l)
{
	node *p = l;
	while( NULL != p ) {
		cout<<'\t'<<p->data;
		p = p->next;
	}
}

int main()
{
	printList( createList() );
	return 0;
}
一直存在一个知识盲点: 

第一、 在createList方法中,list l = p语句之后,修改p会导致指针 l 的修改,调试发现不会有这样的问题。原因在于,l 是一个指针,实际上是有独立的内存空间,汇编为证。

 

 以后对p的修改 自然而然不会影响到 l ,l 只是和p 指向了同一个node节点的内存空间,修改p,只是修改p的指向。不会影响 l 的值。

在这段代码里的第二个容易混淆的地方。 是 createList()方法中 声明的变量 l 作用域为 createList 方法内部。 返回时会不会释放内存,导致l 的值变化。事实上 return语句返回是放在 寄存器中返回的,所有的在栈内空间 申请的局部变量内存都会被销毁,除非通过new 在堆上申请出来的内存空间。返回的指针值 l 只是通过寄存器保存了一份返回。

 

 汇编为证。多次验证,发现 都是用eax 保存返回值。 google 了下,http://blog.csdn.net/misskissc/article/details/16822349   相关内容参考这里。

其次,在输出链表的时候需要注意:遍历链表的结束条件,是NULL  !=  p,不是 NULL  != p->next;这个地方很容易犯错,导致最后一个结点遍历不到或者出现内存溢出情况。


其实,最上面的代码可以不那么啰嗦。

#include <iostream>
using namespace std;

typedef struct node {
	int data;
	struct node* next;
}lnode, *list;

list createList(void)
{
	int data;

	node *temp = NULL,*tail = NULL;
	list l = temp;
	
	while ( 1 ){
		cout<<"please Enter Data: (end of 0)"<<endl;
		cin>>data;
		
		if( 0 == data){
			break;
		}

		temp = new node;
		temp->data = data;
		temp->next = NULL;

		if( NULL == l ){
			l = temp;
			temp = l;
		}else {
			tail->next = temp;
			tail = temp;
		}
	}
	return l;
}

void printList(list l)
{
	node *p = l;
	while( NULL != p ) {
		cout<<'\t'<<p->data;
		p = p->next;
	}
}

int main()
{
	printList( createList() );
	return 0;
}

差别不大,但是没那么啰嗦。

下一个议题,第二种方法,

list createList(void)
{
	int data;

	list l = NULL;
	node *temp = NULL,**tail = &l;
	
	while ( 1 ){
		cout<<"please Enter Data: (end of 0)"<<endl;
		cin>>data;
		
		if( 0 == data){
			break;
		}

		temp = new node;
		temp->data = data;
		temp->next = NULL;

		*tail = temp;
		tail = &(temp->next);
	}
	return l;
}
其实就是用二维指针。  关键是 理解 二维指针, 是指向指针的指针变量  这个概念。  通过二维指针 去操作temp节点,就不用每次都去 判断 temp 是否为空,而二维指针 在汇编层,仅仅也是把他当做一个 内存单元来处理, 相比第一个方法  是节省了一个临时变量,在操作上 每次循环节省了一个判空的操作的。


同时  lea 与mov 指令的差别:  mov 是将 右边的地址表达式里面的值取出来,然后传到左边寄存器或内存地址(不能内存地址直接传到内存地址,必须用寄存器充当中介), 而lea 是 直接将计算出来的地址,(不取值)传到左边。 注意区别。



看汇编码, 不管是 * 还是 &,赋值表达式最终都是 修改内存地址, 只是* 是将地址 取到寄存器中,通过 [ 寄存器 + 偏移地址 ] 寻址修改内存,而 &的操作是将内存 取到寄存器里, 从而也可以 理解&为什么不能包含在 充当左值的表达式中。

第三种:

list createList(void)
{
	int data;

	list l= new node;
	l->next = NULL;
	l->data = (int)l;

	node *temp = NULL;
	
	while ( 1 ){
		cout<<"please Enter Data: (end of 0)"<<endl;
		cin>>data;
		
		if( 0 == data){
			break;
		}

		temp = new node;
		temp->data = data;
		temp->next = NULL;

		(( node* )( l->data ))->next = temp;
		l->data = (int)temp;
	}
	return l;
}
添加头结点,用数据域来保存当前节点的地址值  方法比较新,有学习意义。 但是会有头结点。 个人感觉,只是做法比较新颖,实际意义不大。

以上整理自这里:   http://blog.csdn.net/chiichen/article/details/6656595  感谢。


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值