前言:
现在你们对“c语言增删改查学生信息 c语言删除学生”大概比较关切,兄弟们都想要学习一些“c语言增删改查学生信息 c语言删除学生”的相关资讯。那么小编也在网上网罗了一些对于“c语言增删改查学生信息 c语言删除学生””的相关知识,希望你们能喜欢,同学们一起来了解一下吧!很多小伙伴都老是会碰到疑问,其实还是基础没打扎实,这些题如果你不看答案你能知道多少呢?如果还有很多不知道就证明基础没打扎实,如果你还在入门纠结,如果你还在苦恼怎么入门!小编有个建议,可以加小编弄的一个C语言交流基地,大家可以进入交流基地:379249575,里面新手入门资料,可以说从零到项目实战,都是可以免费获取的,还有程序员大牛为各位免费解答问题,热心肠的小伙伴也是蛮多的。不失为是一个交流的的好地方,小编在这里邀请大家加入我的大家庭。欢迎你的到来。一起交流学习!共同进步!小编等你!还有前面没有看的同学最好从程序员教你学C语言(一)开始看哦,尤其是基础还没打扎实的同学!
第六章 结构化数据
简单便捷效率高,是C语言的语法处理方式,C语言的作者对于编程语言中凡是稍微复杂一点的特性,要么就放入了标准库进行处理,要么就干脆没有加进来。放入标准库里的我们只需要简单调用函数的方式就能使用了,比如我们初学时用的最多的printf,这其实就是一个复杂的函数,我们在讲函数的最后才讲到这种参数个数可变的函数;没有加进来的语法特性,我们也可以很容易的使用C语言的原有语法进行扩展,struct几乎就是专门为了进行相关扩展而诞生的,使用它甚至可以实现C语言的面向对象。以前我们要处理一组类型相同的数据时,我们通常都会定义一个数组,但是如果要处理一组类型不同的数据呢?比如说要处理一匹马的数据,一匹马有名字、高度、出生日期等等,这些数据中有些是字符串,有些是数值,这时候就需要结构了。关键字struct能定义各种类型的变量集合,称为结构,并把它们看做一个单元,下面是一个简单的结构声明例子:
struct Horse
{
int age;
int height;
}silver;
这个例子声明了一个结构类型Horse。Horse不是一个变量名,而是一个新的类型,我们可以使用这个新的类型来定义变量(这里的silver就是我们定义的结构体变量)。当然,我们的结构类型名和结构类型变量名的命名方式和我们熟悉的普通变量名相同。Horse结构内的变量名称age和height称为成员或字段。在这个例子中,它们都是int类型。结构成员出现在结构体名Horse后的大括号内。结构的一个实例silver是在定义结构时声明的,它是一个Horse类型的变量,只要使用变量名silver,它都包含两个结构成员:age和height。
下面我们再把Horse结构类型复杂化一点:
struct Horse
{
int age;
int height;
char name[20];
char father[20];
char mother[20];
}dobbin = {24,17,"Dobbin","Trigger","Flossie"};
结构内的成员可以使任何类型的变量,甚至是数组。在Horse结构类型的这个版本中,有5个成员:整数成员age和height,char数组成员name、father和mother。在Horse结构定义的闭括号后定义了一个实例变量dobbin,给dobbin赋初值的方式和数组类似。注意我们不能在Horse结构体里面初始化值,因为这里Horse只是定义了一个模板或称一个蓝图,在我们定义struct Horse dobbin的时候才定义了一个具体的变量dobbin,我们才给dobbin赋了初值。
可以将结构的声明和结构变量的声明分开,取代前面的例子如下:
struct Horse
{
int age;
int height;
char name[20];
char father[20];
char mother[20];
};
struct Horse dobbin = {24,17,"Dobbin","Trigger","Flossie"};
第一个语句定义了结构类型Horse,第二个声明该类型的变量dobbin。结构定义和结构变量声明语句都用分号结束。在dobbin结构成员的初始值中,dobbin的父亲是Trigger,母亲是Flossie。
也可以给前面两个例子添加第三条语句,定义另一个Horse类型的变量:
struct Horse trigger = {30,15,"Trigger","Smith","Wesson"};
这句定义了变量trigger,它其实就是dobbin的父亲,而dobbin的爷爷奶奶就是Smith和Wesson。当然,也可以像普通变量那样在一行语句中声明多个结构变量。例如:
struct Horse piebald,bandy;
这行语句声明了两个Horse类型的变量。和普通变量声明相比,这个声明只增加了关键字struct。为了使代码跟简洁,我们可以使用typedef定义,这样在声明结构变量时就不需要struct关键字了,如下:
typedef struct Horse Horse;
这个语句把Horse定义为struct Horse,我们就可以直接使用Horse来定义Horse类型的变量了:
Horse trigger = {30,15,"Trigger","Smith","Wesson"};
这样就不再需要每次定义结构变量时都使用struct关键字了。
访问结构成员
现在知道如何定义结构和声明结构变量了,还必须能够引用结构的成员,在C语言里我们在结构变量名的后面加一个句点,再加上变量名称,就可以引用结构成员了,比如我们使用dobbin.age=12就可以把dobbin的age重新赋值为12,结构变量名和成员名间的句点称为成员选择运算符,前面我们在初始化trigger时需要以正确的顺序,其实在初始化列表中可以指定成员名,这样就可以忽略顺序了,如下:
Horse trigger = {.height=15,.age=30,.name="Trigger",.mother="Wesson",.father="Smith"};
下面我们来看一个使用结构体的例子:
#include <stdio.h>
typedef struct Horse Horse;
struct Horse
{
int age;
int height;
char name[20];
char father[20];
char mother[20];
};
int main(int argc, char *argv[])
{
Horse my_horse;
printf("Enter the name of the horse:");
scanf("%s",my_horse.name);
printf("How old is %s?",my_horse.name);
scanf("%d",&my_horse.age);
printf("How high is %s (in hands)?",my_horse.name);
scanf("%d",&my_horse.height);
printf("Who is %s's father?",my_horse.name);
scanf("%s",my_horse.father);
printf("Who is %s's mother?",my_horse.name);
scanf("%s",my_horse.mother);
printf("%s is %d years old,%d hands high,",
my_horse.name,my_horse.age,my_horse.height);
printf(" and has %s and %s as parents.\n",
my_horse.father,my_horse.mother);
return 0;
}
这个程序也比较简单,一开始把Horse定义为struct Horse,然后定义了结构体Horse,然后在main函数里给Horse变量my_horse的每个成员输入了值,最后打印出来
结构指针:
结构指针的声明方式和声明其他类型的指针变量相同,例如:Horse *pHorse = NULL;这条语句声明了一个pHorse指针变量,它可以存储Horse类型的结构地址。需要给Horse添加typedef语句来省略struct关键字。没有typedef,就必须把语句写成:
struct Horse *pHorse = NULL;下面来看看具体使用结构指针的例子:
#include <stdio.h>
typedef struct Horse Horse;
struct Horse
{
int age;
int height;
char name[20];
char father[20];
char mother[20];
};
int main(int argc, char *argv[])
{
Horse my_horse = {3,11,"Jimbo","Trigger","Nellie"};
Horse *pHorse = &my_horse;
printf("%s is %d years old,%d hands high,",
my_horse.name,my_horse.age,my_horse.height);
printf(" and has %s and %s as parents.\n",
my_horse.father,my_horse.mother);
printf("%s is %d years old,%d hands high,",
pHorse->name,pHorse->age,pHorse->height);
printf(" and has %s and %s as parents.\n",
pHorse->father,pHorse->mother);
return 0;
}
可以看到我们把结构变量my_horse的地址赋值给了结构指针pHorse,下面就可以使用结构指针了,注意使用结构指针引用结构成员时要使用->符号,->叫做“指向结构体成员运算符”,它是C语言和C++语言的一个运算符,用处是使用一个指向结构体或对象的指针访问其内成员。最后可以看到使用结构变量使用.运算符和结构指针使用->运算符打印的结果是相同的。
结构的嵌套定义:
可以在Horse结构的定义中声明Date结构,如下:
struct Horse
{
struct Date
{
int day;
int month;
int year;
}dob;
int age;
int height;
char name[20];
char father[20];
char mother[20];
};
这个声明将Date结构声明放在Horse结构的定义内,因此不能在Horse结构的外部声明Date变量。当然,每个horse类型的变量都包含Date类型的成员dob。但下面的语句:
struct Date my_date;
会导致编译错误,错误信息会说明Date结构类型未定义。如果需要在Horse结构的外部使用Date,就必须将它定义在Horse结构之外。
结构指针用作结构成员:
假设我们所处的环境是一个大型的牧场,这里好很多批马,我们要把这些马的数据都存储起来使用,我们该怎么做呢,首先会想到的是结构数组,但是在使用时我们会发现结构数组很不好用,因为牧场几乎每天都有马儿老死病死或者卖给其他人,这样我们就需要从原来的数组里删除掉这匹马的信息,同样牧场里会有小马出生,我们也需要在原来的数组基础上添加新增马儿的信息,但是我们的数组长度是固定的,每当我们要增加一个新的数据或删除一个原有的数据时我们都要大费周章。所以前人就发明了一种结构,可以很方便的解决数据的插入和删除操作。我们知道任何指针都可以是结构的成员,包括结构指针在内,结构成员指针可以指向相同类型的结构。例如,Horse类型的结构可以含有一个指向horse类型结构的指针。这样就可以把马匹链接起来,下面我们来看具体的程序:
#include <stdio.h>
typedef struct Horse Horse;
struct Horse
{
int age;
int height;
char name[20];
char father[20];
char mother[20];
Horse *next;
};
int main(int argc, char *argv[])
{
Horse *pDobbin = (Horse *)malloc(sizeof(Horse));
Horse *pTrigger = (Horse *)malloc(sizeof(Horse));
Horse *pJimbo = (Horse *)malloc(sizeof(Horse));
Horse *current;
//pDobbin赋值
pDobbin->age=24,pDobbin->height=17,strcpy(pDobbin->name,"Dobbin"),
strcpy(pDobbin->father,"Trigger"),strcpy(pDobbin->mother,"Flossie");
//pTrigger赋值
pTrigger->age=30,pTrigger->height=15,strcpy(pTrigger->name,"Trigger"),
strcpy(pTrigger->father,"Smith"),strcpy(pTrigger->mother,"Wesson");
//pJimbo赋值
pJimbo->age=3,pJimbo->height=11,strcpy(pJimbo->name,"Jimbo"),
strcpy(pJimbo->father,"Trigger"),strcpy(pJimbo->mother,"Nellie");
//将三个结构指针变量串在一起
pDobbin->next = pTrigger;
pTrigger->next = pJimbo;
pJimbo->next = NULL;
//循环打印链表
for(current = pDobbin;current != NULL;current = current->next){
printf("%s is %d years old,%d hands high,",
current->name,current->age,current->height);
printf(" and has %s and %s as parents.\n",
current->father,current->mother);
}
return 0;
}
可以看到,我们在结构体Horse中添加了新的成员Horse *next;这是一个Horse结构指针,下面的代码中就是通过
pDobbin->next = pTrigger;
pTrigger->next = pJimbo;
pJimbo->next = NULL;
将这三个结构变量串在了一起,它有个更常见的名字,在"数据结构"中叫做"链表",可以看到我们在for循环里,通过current = current->next来不断指向下一个结构指针变量,这样就能遍历打印出了整个链表的信息,pDobbin、pTrigger、pJimbo都叫做链表的节点,循环结束条件的非空判断就是我们上面设定的pJimbo->next = NULL,pJimbo叫做链表的尾节点,因为它的next指向的是NULL。函数malloc 向系统申请分配指定size个字节的内存空间。返回值是这段空间的首地址,返回类型是 void* 类型。void* 表示未确定类型的指针,C,C++规定,void* 类型可以强制转换为任何其它类型的指针。可能有人不太理解为什么要定义结构指针变量,还使用malloc函数,而不是直接定义结构变量,像这样Horse dobbin不是更方便吗?其实是因为malloc申请的空间更方便我们控制,我们只需要调用free(指针变量)就可以释放掉malloc函数申请的动态内存了,如果是普通变量,我们都没法自己控制变量空间的释放,这样就可能会占用多余的内存空间,这在我们马上要说的链表删除操作就会讲到。当然,malloc的功能很强大,使用它也可以实现比如可变长数组这样的东西。下面有个图可以更好的理解链表:
链表的插入和删除:
所谓数据,无非就是增删查改四个操作,就算你以后学了数据库,你也会发现是这样。数组对于查找和修改这两个操作特别方便,因为我们只需要数组名+下标就可以很容易的访问到一个数据,但是如果涉及到增加和删除操作,使用数组就不太方便了;链表则恰恰相反,增加和删除操作很容易,但是查找和修改就反而不如在数组里这么简洁了,链表插入:假设我们的链表是head->A->B->NULL,分为三种情况,插入链首、插入链尾和插入链表中两个节点中间。插入中间的情况,比如我们有节点C要插入A、B之间,那么我们需要操作,A->C,然后是C->B;插入链尾的情况,我们也只需要两步,B->C;C->NULL这样就插入C成为了链表的尾节点;插入链首的情况也差不多。链表删除:假设链表head->A->B->C->NULL,我们要删除节点B,我们需要两步,A->C;free(B),这样就可以把节点B删除并且释放了它占用的内存,这也是我们上面为什么说要使用malloc创建结构指针变量的最主要原因。来看具体的例子:
#include <stdio.h>
typedef struct Horse Horse;
struct Horse
{
int age;
int height;
char name[20];
char father[20];
char mother[20];
Horse *next;
};
int main(int argc, char *argv[])
{
Horse *pDobbin = (Horse *)malloc(sizeof(Horse));
Horse *pTrigger = (Horse *)malloc(sizeof(Horse));
Horse *pJimbo = (Horse *)malloc(sizeof(Horse));
Horse *pNew = (Horse *)malloc(sizeof(Horse));
Horse *current = NULL;
//pDobbin赋值
pDobbin->age=24,pDobbin->height=17,strcpy(pDobbin->name,"Dobbin"),
strcpy(pDobbin->father,"Trigger"),strcpy(pDobbin->mother,"Flossie");
//pTrigger赋值
pTrigger->age=30,pTrigger->height=15,strcpy(pTrigger->name,"Trigger"),
strcpy(pTrigger->father,"Smith"),strcpy(pTrigger->mother,"Wesson");
//pJimbo赋值
pJimbo->age=3,pJimbo->height=11,strcpy(pJimbo->name,"Jimbo"),
strcpy(pJimbo->father,"Trigger"),strcpy(pJimbo->mother,"Nellie");
//将三个结构指针变量串在一起
pDobbin->next = pTrigger;
pTrigger->next = pJimbo;
pJimbo->next = NULL;
//循环打印链表
for(current = pDobbin;current != NULL;current = current->next){
printf("%s is %d years old,%d hands high,",
current->name,current->age,current->height);
printf(" and has %s and %s as parents.\n",
current->father,current->mother);
}
//pNew赋值
pNew->age=7,pNew->height=12,strcpy(pNew->name,"Lucy"),
strcpy(pNew->father,"pDobbin"),strcpy(pNew->mother,"Meluk");
//插入操作,这里我们将新节点pNew插入到pDobbin和pTrigger中间
pDobbin->next = pNew;
pNew->next = pTrigger;
//删除操作,这里我们将节点pTrigger删除
pNew->next = pTrigger->next;
free(pTrigger);
//再次循环打印链表
for(current = pDobbin;current != NULL;current = current->next){
printf("%s is %d years old,%d hands high,",
current->name,current->age,current->height);
printf(" and has %s and %s as parents.\n",
current->father,current->mother);
}
return 0;
}
我们在上面就插入了新节点pNew并且删除了原来的节点pTrigger。其实我们的程序大可不必定义那么多的指针变量pDobbin、pTrigger、pJimbo,我们可以直接使用一个指针变量比如Horse *pNode = NULL就可以了,大不了我们每次创建新节点时使用pNode = (Horse *)malloc(sizeof(Horse))就可以了,这样因为每次pNode都会指向新的malloc分配的地址,所以我们就相当于创建了一个新节点;另外我们的链表的创建、遍历、插入、删除等等操作都可以单独做成不同的函数,放在另一个源文件里,这样更体现了程序的结构化,这也是我们学过程化程序设计语言的原因。可能你觉得这些不重要,事实上它们大有用处,我们以后要学到的数据结构和算法,就是这样做的
今天就到这里,欲知后事如何且听下回分解(手动滑稽)~
标签: #c语言增删改查学生信息 c语言删除学生