一、 结构体

1. 结构体一般定义格式

(1)类型说明时,定义变量

struct 类型名{

​ …

}变量名;

struct student{
    int num;
    char name[5];
}stu;

(2)先定义类型,再说明变量

struct 类型名{

​ …

};
struct 类型名 变量名;

struct student{
	int num;
	char name[5];
}
struct student stu;

(3)没有类型名,直接定义变量

注意:这种方法在实际编程中用得很少,但是选择题会考到

struct {

​ …

}变量名;

struct {
	int num;
	char name[5];
}stu;

2. 把typedef用户自定义数据类型名称用在结构体

typedef可以用来为结构体起一个新别名,例如

struct student{
	int num;
	char name[5];
};
typedef struct student STU;

这里的STU就是struct student的别名了

(struct student a;) 等价于 (STU a;)

通常也可以将typedef与结构体写在一起,即

typedef struct student{
	int num;
	char name[5];
}STU;

结构中可以包含指向它的指针

typedef struct student{
	int num;
	struct student *p;
}STU;

但是,注意!!!

typedef struct student{
	int num;
	STU *p;
}STU;

这里把struct student *p改为了STU *p,编译器会报错

我们利用typedef为struct student建立别名STU,但在结构体类型本身没有建立完成时,这个别名STU还不存在,编译器不认识STU,所以会报错。

因此,我们可以先typedef,再struct。

typedef struct student;
struct student{
	int num;
	STU *p;
}STU;

虽然这样操作可以,但不规范,还是推荐用

struct student *p;

3. 结构体成员的引用

这里定义一个结构体

typedef struct student{
	int num;
	char name[5];
}STU;

(1)普通变量

用 . 来引用,例如

STU x;
x.num = 4;

(2)指针变量

指针名->成员 或者 (*指针名).成员

*注意这里 . 的运算优先级高,应该把(指针名)括起来

STU *p;
p->num=5;
//or
(*p).num = 5;

(3)数组变量

数组名[下标].成员

STU a[4];
a[2].num=6;

二、链表

链表由数据域和指针域(下一个数据的地址)组成,这里地址域是指针,这个指针是整个数据类型

typedef struct node{
    int s;  //数据域,类型可以是int double char
    struct node *next;  //指针域,类型是struct node * ,即整个数据类型
}NODE;  //有了typedef,这里的NODE相当于struct node的别名,一般要大写以区分。

1. 建立链表

NODE * createlist(int a[]){  //此函数类型必须是NODE*,因为要返回指针h。外部传进来一个数组,长度为N
	NODE *h,*p,*q;  //h是这个链表的头结点,指向链表开头。
	int i=0;
	h = p = (NODE*)malloc(sizeof(NODE));  //开辟内存。(NODE*)是强制转换类型;malloc()是分配内存;malloc(sizeof(NODE))是开辟结构体NODE大小的内存空间
	p->s = 0;  //为头结点为空 0
	while(i<N){
		q = (NODE*)malloc(sizeof(NODE));  //新开内存创建结点,原理同上
		q->s = a[i++];  //为q所在地址的s成员赋值为a[i] ,i再自增
		p->next = q;  //把q的地址给p的地址域
		p=q;  //p再移到q的位置
	}
	p->next = NULL;  //链表结束,最后一个地址域是NULL 空
	return h;  //返回链表头的地址
}

2. 输出链表

void outlist(NODE *h)  //外部传入链表的起始地址
{
	NODE *p;
	p = h;//这样做会输出头结点的值(0)
    //p=h->next; 从首元结点开始输出,不输出头结点
	while (p){  //等价于 while(p!=0)
		printf("%d ",p->s);  
		p=p->next;
	}
}

3. 插入一个数据

现在有a b d e f 五个结点,要把c结点插入b d之间。

分析:原先b的地址域指向d的地址,只需要把c地址域指向d,b地址域指向c就行了。

代码实现

void insert(NODE *h){
    NODE *p=h,*q;
    int i,add=2; //add是插入位置,这里是指在在第二个结点之后插入
    int data = 5; //假定这是要插入的数据
    for(i=0;i<add;i++)
        p=p->next;   //循环add次后结束,即p到了结点b
    q=(NODE*)malloc(sizeof(NODE)); //为结点c开辟空间
    q->s = data; //给结点c的数据域赋值
    q->next=p->next; //从结点b的地址域拿到d的地址,让结点c的地址域指向结点d
    p->next=q;//把结点c的地址给结点b的地址域
}

4. 删除链表中的数据

现在有a b c d e f六个结点,要删除d结点

分析:原先b结点指向c,c结点指向d,只需要用一个临时变量temp保存c结点中存的d结点的地址,再把temp赋值给b结点的地址域。

代码实现

void del(NODE *h){
	NODE *p=h,*temp;
	int i,rmv=3; //rmv是需要删除位置的上一个结点
	for(i=0;i<rmv;i++)
		p=p->next; //循环rmv次后结束,即p到了结点c
	temp=p->next; //暂存删除位置的结点
	p->next=p->next->next; //把结点e的地址赋值给结点c的地址域
	free(temp); //释放被删除的结点,防止内存泄漏
}

5. 完整代码实现

#include <stdio.h>
#include <stdlib.h>
#define N 5
typedef struct node{ 
	int s;
	struct node *next;
}NODE;

NODE * createlist(int a[]);
void outlist(NODE *h);
void insert(NODE *h);
void del(NODE *h);

int main(){
	int a[N]={1,3,5,7,9};
	NODE *h;
	h = createlist(a);
	printf("原始链表:");
	outlist(h);
	insert(h);
	printf("插入:");
	outlist(h);
	del(h);
	printf("删除:");
	outlist(h);
}

NODE * createlist(int a[]){
	NODE *h,*p,*q;
	int i=0;
	h = p = (NODE*)malloc(sizeof(NODE)); 
	p->s = 0;
	while(i<N){
		q = (NODE*)malloc(sizeof(NODE));
		q->s = a[i];
		i++;
		p->next = q;
		p=q;
	}
	p->next = NULL;
	return h;
}

void outlist(NODE *h){
	NODE *p;
	p = h->next;
	while (p){
		printf("%d ",p->s);
		p=p->next;
	}
	printf("\n\n");
}

void insert(NODE *h){
	NODE *p=h,*q,*temp;
    int i,add=2; 
    int data = 6;
    for(i=0;i<add;i++)
        p=p->next;   
    q=(NODE*)malloc(sizeof(NODE)); 
    q->s = data; 
    q->next=p->next; 
    p->next=q;
}

void del(NODE *h){
	NODE *p=h,*temp;
	int i,rmv=3; 
	for(i=0;i<rmv;i++)
		p=p->next; 
	temp=p->next;
	p->next=p->next->next; 
	free(temp);
}