关于被调函数形参:SqList L、SqList &L、SqList *L 的区别
·
C语言中没有引用,只有取地址&
C++语言中有引用&,也有取地址&
(引用就是给变量起别名,对别名的操作就是对原变量的操作)
首先要明白的两点:
- 对顺序表的取值、查找操作,不需要对顺序表进行修改
- 对顺序表的初始化、插入、删除操作,需要对顺序表进行修改
原结构体
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20 /* 存储空间初始分配量 */
typedef int ElemType; /* ElemType类型根据实际情况而定,这里假设为int */
typedef struct
{
ElemType data[MAXSIZE]; /* 数组存放顺序表的数据元素 */
int length; /* 顺序表的当前长度 */
}SqList;
1.某被调函数不需要对原结构体中的 data 和 length 进行修改时,形参就使用
SqList L //某被调函数不需要修改原结构体,使用原结构体当中的数据
2.某函数需要对原结构体中的 data 和 length 进行修改时,形参就使用
SqList *L //某被调函数中的操作要修改原结构体中数据(也可以使用原结构体数据)
//C++中使用引用&
SqList &L //这里的L为结构体的别名,被调函数中对结构体的操作就是对原结构体的操作
上述两个形参的类型定义相当于拥有了将改变后的数据进行”回传“的功能
(在函数结束后销毁变量的情况下,将改变后的数据保留了下来)
是否需要 “回传” 重点在于主调函数后面的语句中是否需要这个改变了的新数据
个人理解(可能有误)
至于为何它们有类似"回传"的功能,也就是在函数结束销毁变量后,仍会保留下新数据的功能?
- 因为主调函数将实参的存储地址传给此形参(指针变量*L),而被调函数中的操作是对其存储内容进行改变,其存储地址没有发生变化,所以无论该存储内容如何变化,最终都会根据该变量的存储地址找到最新的存储内容。(In a word,通过存储地址"跟踪"存储内容)
- 引用其实是给变量起的别名,对别名的操作就是对原变量的操作
1.某函数不需要原结构体进行修改时,形参就使用
SqList L
对顺序表的取值操作
#define MAXSIZE 20
typedef int ElemType;
typedef struct
{
ElemType data[MAXSIZE];
int length;
}SqList;
//被调函数
Status GetElem(SqList L,int i,ElemType *e){
//此函数没有对L中的data或length作出改动,使用普通类型L,无需"回传"给主调函数变化后值,因为没有改变
//此函数对e作出了改动,需要"回传"给主调函数,所以使用指针类型*e
if(L.length==0 || i<1 || i>L.length)
return ERROR;
//对e进行了改动(初始化了e)
//解引用 e 以存储数据
*e=L.data[i-1];
return OK;
}
//主调函数
int main(){
...
SqList L;
ElemType e;
GetElem(L,5,&e); //&e将变量e的存储地址传给被调函数的指针变量*e来保存,从而获取到被调函数改动后的e的值
printf("第5个元素的值为:%d\n",e); //这里e为改动后的值("回传"的新e)
...
return 0;
}
对顺序表的查找操作
typedef struct
{
ElemType data[MAXSIZE]; /* 数组,存储数据元素 */
int length; /* 线性表当前长度 */
}SqList;
int LocateElem(SqList L,ElemType e){
//该被调函数并未对L、e作出改动,无需"回传",所以使用普通类型
int i;
if (L.length==0)
return 0;
for(i=0;i<L.length;i++)
{
if (L.data[i]==e)
break;
}
if(i>=L.length)
return 0;
return i+1;
}
int main(){
SqList L;
int j,k;
....
for(j=3;j<=4;j++)
{
k=LocateElem(L,j); //此函数返回的是i+1
if(k)
printf("第%d个元素的值为%d\n",k,j);
else
printf("没有值为%d的元素\n",j);
}
....
return 0;
}
2.某函数需要对表内数据元素进行修改时,形参就使用
SqList *L //可以修改原结构体当中的数据
SqList &L //C++中使用&
//被调函数中形参&L是主调函数实参变量的别名,被调函数中对形参的改变就是对实参变量的改变
对顺序表的初始化操作
Status InitList(SqList *L){ //此函数对形参作出了改动,将更改后的数据"回传"给主调函数的实参
//也可以在C++中使用&引用 Status InitList(SqList &L)
//修改了结构体中的length,需要"回传",因为主调函数后面需要输出L.length
L->length=0; //等价于 (*L).length=0 解引用L获得其对应的存储内容
return OK;
}
int main(){
...
SqList L;
Status i;
//可以在C++中使用 i=InitList(T);
i=InitList(&L);
//&L:获得L的存储地址,将此存储地址传给被调函数中的指针变量*L,从而获取到被调函数改动后的L(所以可以理解为"回传")
printf("初始化L后:L.length=%d\n",L.length); //这里L为改动后的值
...
return 0;
}
对顺序表的插入操作
Status ListInsert(SqList *L,int i,ElemType e){
//此被调函数中没有对e作出改动,无需 "回传" 给主调函数的实参
//此被调函数中对L作出了改动,需要将更改后的数据回传给主调函数的实参,故不能使用普通类型(无法“回传”)
...
if (i<=L->length)
{
for(k=L->length-1;k>=i-1;k--)
L->data[k+1]=L->data[k]; //对结构体中的data进行了改动
}
L->data[i-1]=e;
L->length++;
return OK;
}
int main(){
SqList L;
Status i;
int j;
for(j=1;j<=5;j++)
i=ListInsert(&L,1,j);
//&L:获得L的存储地址,将此存储地址传给被调函数中的指针变量*L,从而获取到被调函数改动后的L(所以可以理解为"回传")
//这里主调函数中的实参 j 直接传入被调函数中的形参e,被调函数中未对e进行改动,不必"回传"新数据给这里的主调函数
printf("在L的表头插入0后:L.data=");
ListTraverse(L); //对每个数据元素输出
printf("L.length=%d \n",L.length); //这里的L是更改过其data之后的那个顺序表("回传"的新L)
...
return 0;
}
对顺序表的删除操作
Status ListDelete(SqList *L,int i,ElemType *e){
//此被调函数对L和e均作出了改动
int k;
if (L->length==0)
return ERROR;
if (i<1 || i>L->length)
return ERROR;
*e=L->data[i-1];
if (i<L->length)
{
for(k=i;k<L->length;k++)
L->data[k-1]=L->data[k]; //对结构体中的data进行了改动
}
L->length--;
return OK;
}
int main(){
SqList L;
ElemType e;
...
...
j=5;
ListDelete(&L,j,&e); /* 删除第5个数据 */
//&L:获得L的存储地址,将此存储地址传给被调函数中的指针变量*L,从而获取到被调函数改动后的L(所以可以理解为"回传")
//&e:获得e的存储地址,将此存储地址传给被调函数中的指针变量*e,从而获取到被调函数改动后的e(所以可以理解为"回传")
printf("删除第%d个的元素值为:%d\n",j,e); //这里的e为"回传"的改变后的数据
printf("依次输出L的元素:");
ListTraverse(L); //对每个数据元素输出
//这里的L是更改过其data之后的那个顺序表("回传"的新L)
...
return 0;
}
总结:
只要被调函数中的操作需要改变结构体中的 data 或 length
被调函数的形参就要使用
SqList *L
在C++中使用&
SqList &L
如果不需要改变结构体中的 data 或 length
被调函数的形参使用
SqList L
更多推荐
已为社区贡献2条内容
所有评论(0)