在考研过程中用伪码学习的数据结构,本来想用C语言把这些数据结构全部实现一遍,但是一开始就卡再了一个莫名其妙的地方。标题中说的复杂并不是指这种用法多高级,反而是特别基础需要对基础概念特别清晰。
key work:指针、结构体、函数传参、指针类型参数
我想要实现一个顺序表示的线性表,在这本书(严蔚敏的数据结构清华大学出版社)中称为线性表的顺序表示,Sequential Linear List(以下简称SqList)。我觉得在Java中对应的就是ArrayList。赘述一下线性表示,线性表示就是用一组地址连续的存储单元一次存储线性表的数据元素。C语言中有两个东西符合要求,一个是数组(Array)另一个是指针变量。先放一张书的原图:
这就是书上伪码,就以它为例,阐述一下我遇到的问题
先定义一下结构体
typedef struct
{
char* info;
}ElemType;
typedef struct
{
ElemType *elem;
int* temp;
int length;
int listsize;
}SqList,*Sq;
先说关于结构以需要知道的东西:
typedef
的作用就是创造了一种新的类型来代表一个或一组已知的类型。所以对于第一个结构体可以认为ElemType
就是char*
.而第二个结构体则表示有两种变量名可以表示这个结构体,一种是普通类型SqList
另一种则是指针类型*Sq
(注意这里星号‘*’在前)
Sqlist name1;
Sqlist *name2;
sq name3;
sq *name4;
name1表示这个结构体的实体变量,name2和name3是完全相同的东西都是一个这个结构体的指针。而name4本身就是指针类型再次声明为指针类型,所以它是一个可以指向“这个结构体指针”的指针。所以以下操作都是正确的:
name2 = & name1;
name3 = name2;
name4 = & name3;
关于指针的操作有两个特殊的运算符*
与&
第一个*
有两个使用的地方,一个是在声明指针变量或传参的时候起标识作用,另一个是在获取指针所指的内容的时候,作为运算符起作用
int temp = 2;
int *a = &temp;
*a = 3;
printf(%d\n,temp);
运行结果就是temp的值变为了3,第二行的第一个星号*
仅仅表示对指针的声明,而第三行第二个星号才表示所指变量的内容。一定要分开来看不然就解释不通了。
然后是关于线性表的一个初始化函数:
void InitList(Sq list){
//construct a empty sequential list
printf("here1\n");
list->elem = (ElemType * )malloc(100*sizeof(ElemType));
printf("here2\n");
list->length = 0;
(*list).listsize = LIST_INIT_SIZE;
}
首先的一点是关于伪代码中捕捉申请动态空间不成功的部分,不需要考虑
list->elem = (ElemType * )malloc(100*sizeof(ElemType));
if(list->elem == NULL){
exit(0);
}
因为如果没有分配成功,指令台上就会直接显示段错误
从而停止运行,所以就算写了也不会运行。真的不用理。
关于指针参数的作用,一般参数传递机制是复制,也就是说函数中进行的运算不会改变参数。而如果使用指针参数,在函数中修改形式参数的变量时,实际上就是对实际的参数进行的操作。
InitList(Sqlist list){
... ...
}
//一般参数
InitList(Sqlist *list){
//*的第一种用法,标识作用
... ...
}
//指针参数
InitList(Sq list){
//这里虽然没加*,但是Sq本来就在`typedef`的时候别标成了指针
... ...
}
//指针参数
第二点要说的是,对于一个指针类型的结构体,想要访问其内部的属性元素时有两种方法:
InitList(Sqlist *list){
list->elem;
(*list).elem;
... ...
}
注意(*list).elem
一定要加括号,因为.
的优先级要高于*
, 这里需要的是结构体对象所以需要*
还是比较好理解的。而书中伪代码的写法list.elem
是有语法错误的,通不过编译的。
这样写好了函数和参数,但在主函数中如何传参也是很重要的,而且一般书都省略的这部分,我也就是在这出错的。
int main(int argc, char const *argv[])
{
SqList list;
Sqlist *list2;
InitList(&list);
printf("success!\n");
}
对于InitList()的参数,可能有三种添法:
InitList(list);
InitList(list2);
InitList(&list);
第一种看起来像直接传参,但是函数的参量是一个指针类型,所以并不匹配,编译都通过不了。
第二种,需要的是指针类型,我填的也是指针类型,看起来很对呀。但是我忘了,任何指针类型想要使用必须先初始化,也就是需要分配地址空间。换句话说就是必须需要一个实体类型,所以如果非要这么写,变成下面这样也是对的:
int main(int argc, char const *argv[])
{
SqList list;
Sqlist *list2;
list2 = & list;
InitList(list2);
printf("success!\n");
}
第三种是标准写法,需要的是一个指针类型,而指针类型的本质是储存地址的变量,所以用取地址符&
传给它一个地址是真确的,也是标准的操作方法。
后记:
这是我第一次写这样的博客,博自己遇到的问题写下来总结出来,为以后自己遇到问题做题个提醒,省的自己“记吃不记打”。这文章还是比较基础的,如果能帮助到看这边文章的你,我也会很开心的。