【原创】一文搞懂严蔚敏数据结构迷人的SqList &L和SqList L、ElemType &e和ElemType e

旁白

最近小渔夫在看严蔚敏、李冬梅《数据结构 c语言版》(第2版),学到第二章顺序表的实现时,看到函数参数一会是SqList &L、一会又是SqList L、一会ElemType &e、一会又ElemType e,当场大写的黑人问号加感叹号。这都什么玩意,一会有&一会又没有,都代表什么意思呢?

于是带着这些问号去找答案,上网上看了很多,看到的比较零散,于是我整理了一下,理清原因后,心想估计也有同学跟我一样的黑人问号,于是就有着这篇文章,希望能有点帮助吧。

先说答案

严书里的代码是伪代码,什么是伪代码?顾名思义不是真的代码,拿到电脑上去跑不起来的代码,伪代码重点是表达思路、表达想法的。

所以,书里函数参数中有&,想表达的意思是:希望通过函数改变该参数的值。可以看到书里在新建顺序表、插入一个数据、删除数据等改变顺序表的操作时用到了SqList &L,因为这些操作会改变表的内容;在查找操作时,没有改变表,顺序表前没有&,用到了SqList L;在取值时用到了ElemType &e,是想把取到的值通过参数返回。

C语言里只能通过指针实现。书里写的是伪代码,而不是真正的C代码;在C++里可以通过指针和引用(C语言无引用)实现。

有些同学可能不太理解怎么通过函数改变传进来参数的值,下面详细介绍一个例子就明白了。

详细介绍

例子是用c语言的指针实现的,说起指针不得不提两个符号*&,那先来看指针中的*&

C语言指针中的*&

首先,一般看一个变量,看它的3点。变量名称、数据、地址。如定义一个变量int a = 5,变量名是a,值是5,在内存中的地址是0x00001111
image
了解了变量,就可以了解指针了,指针可以理解成内存里的地址,指针变量就是存储地址的变量。

&是取地址运算符,用于取变量地址;例如:int a = 5, &a表示变量a的地址0x00001111

*出现在不同的地方含义不同,但就我现在理解的,一般出现在两个地方:

  • 函数参数中和变量定义中,表示定义一个指针变量。如 int *p ,int *p = &a.
  • 等号右边,*(地址)表示取值运算。如int a = 5; int *p = &a; *p,中变量啊的值为5,指针变量p的值为0x00001111*p的值为5

例子:交换两变量的值

了解了指针后,来看下这个例子。例子中写了两个函数,来交换两变量的值,第一个函数swap1没用指针,第二个函数swap2用了指针。结果你猜,哪个函数能改变传进来参数的值?

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

int main(int argc, char *argv[]) {
	int num1 = 1;
	int num2 = 2;
	swap1(num1, num2);
	printf("num1 = %d, num2 = %d\n", num1, num2);
	swap2(&num1, &num2);
	printf("num1 = %d, num2 = %d\n", num1, num2);
	// test();
	return 0;
}

void swap1(int a, int b) {
	int temp;
	temp = a;
	a = b;
	b = temp;
}

void swap2(int *a, int *b) {
	int temp;
	temp = *a;
	*a = *b;
	*b = temp;
}

如图:运行一下,得到结果。第二个函数起作用了,也就是用了指针的函数能改变传进来参数的值,实现交换两变量的效果。这就是通过函数改变传进来参数的值

执行结果

到这里,已经得到答案了。

至于,为什么第一个函数没有实现交换,有兴趣的再往下看看。

单步调试一下,我发现了其中的奥妙。

调试开始,如下图,变量num1值为1,地址为0x62fe1c,变量num2值为2,地址为0x62fe18

为什么1

往下走,开始调用函数,进到函数swap1里。如图,变量num1num2没什么变化。参数ab已经接收到传进来的参数了。同时发现num1num2的 地址和ab不一样,原来函数会把形式参数当作局部变量,然后在栈中开辟内存空间,用于存放由主调函数传递进来的实参值,从而形成了实参的一个副本(替身)。

为什么2

再往下走,走完发现了惊喜,与上图相比ab互换了。而变量num1num2和还是没什么变化。

为什么3

最后,函数swap1执行结束,输出结果num1 = 1, num2 = 2。可以发现虽然变量交换了,但是只是交换了副本(替身)。至于为什么用了指针就可以交换真身呢?感兴趣,动动小手去探索探索吧,哈哈哈。

image

附录

1. *p和**p的区别

int *p 是一级指针,存放的是一个变量的地址。

int **p是二级指针,存放的是指针变量的地址。

例子:

//定义整形变量
int a = 6;     
//定义一个指针指向这个变量
int *p = &a;   
//定义一个二级指针指向p指针
int **pp = &p; 
// 那么取出6的方式都有哪些呢?
printf("a=%d", a);
printf("a=%d", *p);
printf("a=%d", **pp);

以上3行输出的值都是6 。

2. *&p和&*p的区别

根据单目运算符运算的优先级,*&p 等价于*(&p)&*p 等价于&(*p)

  • 如果p是指针变量,那么*&p = p&*p = p,都等于p,但还没定义p指向哪,存的是谁的地址。

  • 如果p是一个int变量,那么*&p = p;而&*p是非法的,因为*p非法。

比如int p =10;那么*&p = *(&p) = p = 10(即从p的地址取值),而&*p = &(*p) 则非法,因为p=10,*10是取内存地址为10的值,这在c语言中是不合法的。

后续

我在学习中发现,c只能通过指针实现这种方式,而C++不仅能通过指针实现,还能通过引用实现。并且C++还提倡使用引用。本来想继续写写,什么是C++引用,为什么C++引进这个概念,这个概念有什么好处、C++指针参数和引用参数有什么区别的。但发现跟主题好像离得有点远,扯到C++去了,作罢。看看后面有机会用C++实现算法的时候再行补上。

以上个人拙作,希望能给路上同仁带去点光。

若不小心对你有启发,评论留下你的故事,点赞分享让更多人为你受益。

posted @ 2021-04-27 12:55  码头小渔夫  阅读(6122)  评论(2编辑  收藏  举报