C/C++链表操作函数传参为结构体指针,无法修改实参的值?

在写链表的代码时候遇到的一个问题,首先链表节点的结构体定义为

typedef int ElementType;
typedef struct Node *PtrToNode;
struct Node {
	ElementType Data;
	PtrToNode   Next;
};
typedef PtrToNode List;

然后在实现一个链表合并(Merge)的功能时,函数的实现如下,在这个函数执行的过程中,在函数体最后明确将L1/L2(链表的头指针)都置为了NULL,但是返回主函数中的L1/L2却仍然是函数传参前的L1/L2的值,百思不得其解,明明我传的是指针啊?

List Merge( List L1, List L2 )
{
    // 新链表的头结点
    List head = (List)malloc(sizeof(struct Node));
    List node = head;
    // 直接用L1/L2遍历链表,先让L1/L2指向首节点
    L1 = L1->Next;
    L2 = L2->Next;
    while(L1 && L2)
    {
        if(L1->Data >= L2->Data)
        {
            node->Next = L2;
            node = L2;
            L2 = L2->Next;
        }
        else
        {
            node->Next = L1;
            node = L1;
            L1 = L1->Next;
        }
    }
    // L2结束,L1还未结束
    if(L1)
    {
        node->Next = L1;
    }
    // L1结束,L2还未结束
    if(L2)
    {
        node->Next = L2;
    }
    L1 = NULL;
    L2 = NULL;
       
    return head;
}

想了一会儿想不明白,然后就写了个简单的程序试试,程序如下(VS2015),func的参数为int类型指针,实参为指向a的指针ptr,然后调试运行一下

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

void func(int* p);

int main()
{
	int a = 2;
	int* ptr = &a;
	func(ptr);
	printf("%d\n", *ptr);

	system("pause");
	return 0;
}

void func(int** p)
{
	p = NULL;
}

main函数中的ptr
func中的p
这里看到,ptr和p的是相同的,这里应该看出来一些端倪了,即p是ptr的一份拷贝,而并不是ptr本身,这里我们可以再取地址看一下这两个变量是不是同一个,程序如下

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

void func(int* p);

int main()
{
	int a = 2;
	int* ptr = &a;
	int** pptr = &ptr;
	func(ptr);
	printf("%d\n", *ptr);

	system("pause");
	return 0;
}

void func(int* p)
{
	int** pp = &p;
	p = NULL;
}

在这里插入图片描述
在这里插入图片描述
pptr和pp分别是指向ptr和p的指针,也就是ptr和p的地址,上面两图可以看出,pptr和pp并不是同一个值,也即ptr和p不在内存的同一位置,ptr和p不是同一个东西,所以在函数中修改p=NULL只是把ptr的一个拷贝改成了NULL,而没有改变main函数中的ptr,所以返回之后ptr仍然指向2。
但是如果再将程序修改一下,将func的参数改为int** p,再次运行,程序如下

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

void func(int** p);

int main()
{
	int a = 2;
	int* ptr = &a;
	func(&ptr);
	printf("%d\n", *ptr);

	system("pause");
	return 0;
}

void func(int** p)
{
	*p = NULL;
}

这个时候再来看,可以看到func函数已经将ptr置为了NULL,同时func中的p也是指向了ptr,所以这时才修改了main函数中的ptr,有点绕~
然后回到链表合并的函数

List Merge( List L1, List L2 )
{
    // 新链表的头结点
    List head = (List)malloc(sizeof(struct Node));
    List node = head;
    // 直接用L1/L2遍历链表,先让L1/L2指向首节点
    L1 = L1->Next;
    L2 = L2->Next;
    while(L1 && L2)
    {
        if(L1->Data >= L2->Data)
        {
            node->Next = L2;
            node = L2;
            L2 = L2->Next;
        }
        else
        {
            node->Next = L1;
            node = L1;
            L1 = L1->Next;
        }
    }
    // L2结束,L1还未结束
    if(L1)
    {
        node->Next = L1;
    }
    // L1结束,L2还未结束
    if(L2)
    {
        node->Next = L2;
    }
    L1 = NULL;
    L2 = NULL;
       
    return head;
}

同理List是链表节点结构体的指针

typedef struct Node *PtrToNode;
struct Node {
	ElementType Data;
	PtrToNode   Next;
};
typedef PtrToNode List;

在传递时虽然传入了链表的头指针,但在Merge函数中的L1却只是头指针的一个拷贝,所以最后的代码L1=NULL并不会修改main函数中的链表头指针,所以代码可以修改如下,虽然L1是头指针的拷贝,但是L1->Next却是指向首节点的指针(这个指针与main函数中的指针是相同的),将这个指针置为NULL,可以改变main函数中的L1->Next

List Merge(List L1, List L2) {
	// 新链表的头结点
	List head = (List)malloc(sizeof(struct Node));
	List node = head;
	// 直接用L1/L2遍历链表,先让L1/L2指向首节点
	List p1, p2;
	p1 = L1->Next;
	p2 = L2->Next;
	while (p1 && p2)
	{
		if (p1->Data >= p2->Data)
		{
			node->Next = p2;
			node = p2;
			p2 = p2->Next;
		}
		else
		{
			node->Next = p1;
			node = p1;
			p1 = p1->Next;
		}
	}
	// L2结束,L1还未结束
	if (p1)
	{
		node->Next = p1;
	}
	// L1结束,L2还未结束
	if (p2)
	{
		node->Next = p2;
	}
	L1->Next = NULL;
	L2->Next = NULL;

	return head;
}

也可以用一幅图来表示这两个指针的关系
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章