(1)声明类型
struct tag { member-list; } variable-list; 声明结构体类型
其中,struct tag 即为创建的结构体类型;tag为结构体标签。
member-list表示结构体中的所有成员变量;variable-list表示定义出的全局结构体变量,可写可不写。
typedef struct tag { member-list; } typename; 声明结构体类型并重命名
添加typedef前缀之后,rename即为创建的结构体类型。
(2)定义变量
struct tag s1 = { ...... };
typename s1= { ...... }; 两种方式都可以创建结构体变量
s1为定义出的局部结构体变量。
同理,若s1为嵌套结构体,则定义结构体变量的语法为:typename s1 = { ...{ .... }... };
(1) 直接访问成员
typename s1 ;
s1 . member ;
s1为结构体变量,member为结构体类型声明里的成员变量名。
同理,若s1为嵌套结构体,则访问最内部成员变量的语法为:s1.s2.member ;
(2) 通过指针访问成员
typename s1; typename * p = &s1;
p -> member ;
p为存储结构体首地址的指针变量。
注:对于结构体函数传参而言,运用指针进行地址传参显然更优,因为只用开辟4Byte的内存用于储存首地址,而直接传参需要开辟与原结构体等大的内存。(函数传参时,参数需要压栈,结构体过大时压栈的系统开销较大,导致系统性能的下降)此外,在数据结构的接口函数中,必须用访问地址的方式对实参进行改变。
当我们使用结构体数组等占用内存大但不知道分配多大空间的数据类型时,需要使用动态内存分配的相关函数。需要调用的头文件是:
(1) malloc函数
int *p = (int*) malloc (10 * sizeof (int) ); 向内存申请10个整型的连续空间
malloc函数的返回值是一个指针(若内存分配失败则返回空指针)。此时p存放的是分配的动态内存空间的地址。
(2) free函数
free ( p ); 回收空间
当动态申请的空间不再使用时,应该还给操作系统。free(p)后,p指向的地址不变。因此应该在添加:p = NULL;防止野指针的出现。
此外还有realloc(重新分配内存)、calloc(和malloc相似,但会使分配的内存数据置为0)等内存分配函数。
(1)静态顺序表(数组)
typedef struct SeqList
{ int a[n]; int size; } SL; a[n]存储数组,size表示数组中存储数据个数
而用于该数据结构输入、输出、修改的函数被称为接口函数,如:
void SeqListPushBack(SL * ps ,SLDataType x);
接口函数的形参一般都是指针变量,方便通过访问地址的方式修改实参。
静态顺序表的特点是数据会被存满,因此不方便定义存储的空间大小。定义过大会造成空间的浪费。
(2)动态顺序表
typedef struct SeqList
{ int *p; int size; int capacity } SL;
将定义的数组改成指针,新增capacity表示数组能存放的空间容量
例如,当顺序表已经存储五个数据{0,1,2,3,4}时,size = 5;而此时还可以存放2个空间,则capacity=7,若size = capacity则进行增容。p[n]表示存储的第(n-1)个数据。
显然,在动态顺序表中,我们分配内存的地址在SL.p中,结构体只是帮助记录数组信息。
动态顺序表的接口实现:
接口函数包括初始化、尾插数据、尾删数据等。这里以尾部添加数据为例。
函数声明:
void SeqListPushBack ( SL *slp, SLDataType x){......}
其中slp是指向结构体的指针。x为要添加的数据。SLDataType为顺序表中存放的数据类型。
1.当 (slp->capacity) > (slp->size) 时,只需简单插入数据即可。
slp -> p[ slp ->size ] = x;
slp -> size ++; p[slp ->size]表示当前要存放的数组位置
2.当(slp->capacity) = (slp->size) 时说明数据已存满或该顺序表刚刚初始化,则进行扩容。
int newcapacity = ( slp -> capacity == 0 ?4 :slp->capacity * 2 );
使用三目运算符
表示若此时顺序表容量为0则容量改为4,若为4则容量变为两倍。
然后用realloc函数修改开辟的空间:
slp = (SLDataType * ) realloc ( slp ->p,newcapacity * sizeof(SLDataType));
slp -> p = tmp;
slp -> capacity = newcapacity; 重新分配slp指向的内存空间
由于接口函数大多使用了malloc,realloc等内存分配方式,通常都需要Destroy接口函数来free所有内存空间,销毁顺序表,并将slp->p置空,防止p变成野指针。
顺序表需要频繁扩容,且会造成空间的浪费,且从中间插入或删除数据需要挪动之后所有数据,效率不高。针对顺序表的缺陷设计出链表。
struct SListNode{ SLDateType data; struct SListNode* next ; } ;
单链表由多个结点(node)构成,每个结点是一个结构体。一般每个单链表的头结点数据域存放的是关于该链表的长度等相关数据。每个链表有一个头指针指向头结点。
单链表的接口函数以打印单链表为例:
void SListPrint (SLTNode * phead)
{ SLTNode * cur = phead;
while (cur != NULL)
{ printf("%d->",cur -> data);
cur = cur -> next; }
}
栈的数据存储特点是先进后出。
struct Stack { STDataType * p ; int top ; int capacity; } ;
本质上是将顺序表的size改为pop,不再关注已存储的数量
队列特点是先进先出。
typedef struct QueueNode{ QueueNode* next ; QDataType data ; }QNode ;
typedef struct Queue{ QNode* head ; QNode* tail ; };