您的位置:首页 > 其它

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

2014-01-09 00:48 495 查看
我一般常用的不带头结点的链表初始化:

#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 了下,/article/1935353.html 相关内容参考这里。

其次,在输出链表的时候需要注意:遍历链表的结束条件,是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;
}
添加头结点,用数据域来保存当前节点的地址值 方法比较新,有学习意义。 但是会有头结点。 个人感觉,只是做法比较新颖,实际意义不大。

以上整理自这里: /article/10790794.html 感谢。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: