·C递归调用——栈空间的调用形式不会更改引用传递的数值
本篇关于di'gui's
As we all konw,C的变量空间分为5段——内核区 栈 堆 数据段 代码段,其中栈区是由编译器自动分配释放,存放函数的参数值,局部变量等。
由于这种栈区存储变量数据在函数结束自动释放的机制,导致了函数按值传递时形参无法修改实参,相当于形参复制了一份实参(按值传递)然后最后栈区会把调用的内存在函数调用结束时丢掉。
本质上函数参数传递时是按值传递,如果此时传递的是内存地址(指针 /引用,引用是C++的一个特性,本质是一个常指针,例如:void func(int & x); main() { func(x); }⇔ void(int* const x); main() { func(&x); } 形参为引用的函数体内部,如果直接对引用的变量操作本质上⇔*引用的变量名 也即解引用后操作)
稍微说一下本质,引用就是把一个变量名(基本变量名对应其所在内存的数据区数据,但是要注意:数组的变量名是数组第一个元素的地址区数据)所对应的内存的地址引导出来使用,解引用就是把变量内存存储的数据区上所存储的代表地址的十六进制数解为内存的地址所对应的变量(也即通过解引用导向的内存地址来直接操作这个地址的内存),只不过只有指针这种类型的变量的数据区可以存储地址,所以解引用也只这对与指针。
对于递归调用,其有个最大的特点:虽然是在栈中进行并且你用了指针来传参,但是函数的每个执行过程都在栈中有自己的形参和局部变量的备份,这些备份都是独立的,与函数的其他过程毫不相干,也就是说你在栈内创建了一个又一个新的临时变量来进行相关的代码操作,那肯定会问这创建了临时变量,函数执行完毕后边会弹出,我修改个毛线?不不不,这就是指针来操作内存空间的魅力所在,你是创建了临时变量,但是你创建了的临时变量里面存放的指针,你用这个临时指针修改了内存,这个内存的变化不是临时的!指针他只是你的手段,你通过这个手段来操作内存。
当你第一次调用函数时候,就已经在栈底保存了相关数据,就已经是在玩一个临时的指针,不过指向的内存是真的,所以在使用递归函数以指针/引用方式传参不会改变外面引用的指针指向,但是会对其指向的内存进行操作。
下面是我写的关于递归实现的笔记。
建议大家跟踪调试,至少记录下下面这两个变量在递归过程和返回过程中指针变量的地址和指针变量内容。
// 习题.递归算法实现删除不带头结点的单链表L中所有值为x的节点.cpp 2022_9_27
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
typedef struct LNode //单链表结构体定义
{
ElemType data;
LNode* next;
}LNode, * LinkList;
bool TailCreate(LinkList& L, int length)
{
int element = 0;
LNode* tem = (LNode*)malloc(sizeof(LNode));
scanf("%d", &element);
tem->data = element;
L = tem;
LNode* con_p = tem;
for (int i = 1; i < length; i++)
{
tem = (LNode*)malloc(sizeof(LNode));
scanf("%d", &element);
tem->data = element;
tem->next = NULL;
con_p->next = tem;
con_p = tem;
}
return true;
}
void DeleteRecursion(LinkList& L, ElemType x)//递归采用引用机制不会损伤引用变量的数值
{
LNode* p; //工作指针p用于删除操作,这个p只会在当前递归层级作用
if (L == NULL) //空表,直接return
{
return;
}
else if (L->data == x) //L 结点的data域值为x
{
p = L; //赋值
L = L->next; //向下走
free(p); //释放掉p指向的结构体结点的空间
//进入递归工作,此时L被在栈底保留进行以一个递归函数的入栈,所以当递归操作完全结束时虽然是引
//用头指针L但是L的指向并没有改变,依旧指向第一个节点。
DeleteRecursion(L, x);
}
else //L 结点不是要删除的x结点,向下递归
{
DeleteRecursion(L->next, x);
}
}
int main()
{
LNode* L = NULL; //初始化头指针
TailCreate(L,6);
printf("请输入要删除的元素值\n");
int num;
scanf("%d", &num);
DeleteRecursion(L, num);//调用递归删除值为num的函数
printf("%d", L->data);
return 0;
}