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;
}

也可以用一幅圖來表示這兩個指針的關係
在這裏插入圖片描述

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