声明结构体变量并调用:
格式:
struct 结构体类型名称 结构体变量名;
举例:
//声明结构体变量
int main() {
//struct 结构体类型名称 结构体变量名;
struct student stu1;
struct cat cat1;
return 0;
}
调用结构体:
格式:
结构体变量名.成员名 = 常量或变量值 ;
举例:
//声明结构体变量
int main() {
//struct 结构体类型名称 结构体变量名;
struct student stu1;
struct cat cat1;
//调用:结构体变量名.成员名 = 常量或变量值;
stu1.id = 1;
// stu1.name = "Tom"//报错,针对于数组,不能重新赋值
strcpy(stu1.name,"Tom");//正确的方式,并没有给数组重新赋值
stu1.gender = 'M';
stu1.age = 18;
strcpy(stu1.address,"北京市海淀区中关村");
printf("id = %d,gender = %c,age = %d,address = %s\n",stu1.id,stu1.gender,stu1.age,stu1.address);
return 0;
}
调用的其他写法:
方式2:
#include <stdio.h>
struct Cat {
char *name;
int age;
char gender;
};
int main() {
struct Cat cat1 ={"小黄",2,'M'};
printf("name = %s,age = %d,gender = %c",cat1.name,cat1.age,cat1.gender);//赋值的顺序要和声明的顺序一致
struct Cat cat2 = {"小黑"};//没有赋值的成员,会被赋值为默认值
return 0;
}
方式3:
#include <stdio.h>
struct Cat {
char *name;
int age;
char gender;
};
int main() {
//struct 结构体名 结构体变量 = {.成员1 = xxxx,.成员2 = yyyy……}
struct Cat cat = {.gender = 'F',.age = 2,.name = "小回"};//这种赋值方法不用和声明的顺序一致
printf("name = %s,age = %d,gender = %c\n",cat.name,cat.age,cat.gender);//剩下没赋值的成员会被默认赋值为默认值
return 0;
}
方式4:
//声明类型的同时定义变量
struct sudent {
char *name;
int age;
}stu1,stu2;
stu1.name = "tomo";
stu1.age = 18;
return 0;
方式5:
struct{//匿名结构体
char *name;
int age;
}t1,t2;
t1.name = "tomo";
t1.age = 18;
方式6(就按这种写):
//在格式4的基础上融合格式3的写法
struct person {
char *name;
int age;
}per1 ={.name ="jack",.age = 18},per2 ={.name = "luci",.age = 21};
方式7:
typedef struct student {
char *name;
int age;
}Emp;
Emp stu1,stu2;
return 0;
}
说明:
在创建一个结构体变量后,需要给成员赋值,在没有给成员赋值的情况下使用,打印的值就是垃圾数据,可能导致程序异常终止
不同结构体变量的成员是独立的,互不影响,一个结构体变量成员的修改,不影响另一个
练习:
//练习,编程创建一个box结构体,在其中定义三个成员表示一个立方体的长,宽,高,长宽高可以通过控制台输入
#include <stdio.h>
struct box {
int length;
int width;
int height;
}box1;
//定义一个函数,获取立方体的体积
int box_volume(struct box box1) {
return box1.length * box1.width * box1.height;
}
int main() {
struct box box1;
printf("输入长宽高:");
scanf("%d%d%d",&box1.length,&box1.width,&box1.height);
printf("%d",box_volume(box1));
}
结构体嵌套:
结构体的成员可以是基本数据类型,也可以是数组,指针,结构体等,如果结构体的成员是另一个结构体,这就构成了结构体嵌套
举例1:
//结构体练习
#include<stdio.h>
#include<string.h>
struct Name {
char firstName[20];
char lastName[20];
};
struct student {
int age;
struct Name name;
char gender;
}stu1 = {
.age = 18,
.name.firstName = "wang",
.name.lastName = "zhu",
.gender = 'M'};
int main() {
printf("%d\n%s %s\n%c\n",stu1.age,stu1.name.firstName,stu1.name.lastName,stu1.gender);
return 0;
}
举例2:
//测试结构体嵌套
struct Birthday {
int year;
int month;
int day;
}birthday;
struct employee {
char name[30];
int age;
char gender;
struct Birthday birthday;
}employee1 ={.age = 18,.name = "wangzhu",.gender = 'M',.birthday = {.year = 2020,.month = 4,.day = 8}};
int main(){
printf("%d\n%s\n%c\n%d %d %d\n",
employee1.age,employee1.name,employee1.gender,employee1.birthday.year,employee1.birthday.month,employee1.birthday.day);
return 0;
}
(要打印的数据的名字要写全,一路.过去)
举例3:单链表结构的定义
//单链表结构的定义:
struct node {
int data;//节点中保存的数据
struct node *next;
}node1 = {.data = 10},node2 = {.data = 20,.next = &node1},node3 = {.data = 30,.next = &node2},node4 = {.data = 40,.next = &node3};
node4的指针指向node3,node3的指针指向2,2的指针指向1,1的指针指向NULL值,由此可以得到一个单链表,并且通过node3可以遍历整个列表
举例4:二叉树结构的定义:
//二叉树结构的定义
typedef struct node {
int data;//这里默认是int型
struct node *left;//对应左边
struct node *right;//对应右边
};
结构体占用空间:
结构体占用的存储空间,不是各个属性存储空间之和。
为了计算效率,C语言的内存占用空间一般来说都是int型存储空间的整数倍,如果int类型的是4字节,那struct的就必须是4的整数倍
举例:
//结构体占用空间
#include <stdio.h>
struct A {
char a;
int b;
//int c;
};
int main() {
printf("%d",sizeof(struct A));//输出为8
return 0;
}
struct A 存储的空间不是5个字节,而是8个字节,a和b之间有3个字节空洞
如果加上int c那么就是12个字节。计算的时候是把每个属性都自动补齐成4个字节。
结构体变量的赋值操作:
同类型的结构体变量可以使用赋值运算符=,赋给另一个变量,比如:
struct 1 = struct 2;//假设struct 1和struct 2已定义为同类型的结构体变量
这时会生成一个新的副本,系统会分配一块新的内存,大小与原来的变量相同,把每个属性的复制过去。也就是说,结构体变量的传递机制是值传递,而不是地址传递
举例1;
#include <stdio.h>
struct Car {
char name [10];
double price;
}a = {.name = "BMW", .price = 2342.02};
int main() {
struct Car b = a;
printf("%p\n",&a);//00007ff664fba000
printf("%p\n",&b);//0000003e1fbffa90
printf("%p\n",a.name);//00007ff664fba000
printf("%p",b.name);//0000003e1fbffa90
return 0;
}
也就是说,结构体的复制是将结构体变量完全复制一份,给新的结构体变量。两个结体相互独立,修改一个不会影响另一个。
举例2:
例1有一个前提,就是struct结构的属性必须定义成字符数组,才可以复制数据。如果属性定义成字符指针,结果又不一样。
struct Car {
char *name;
double price;
}a = {.name = "BMW",.price = 2342.02};//这里的.name就是一个指针变量,指向BWM
int main() {
struct Car b = a;//b复制后的b.name也是一个指针变量,指向的也是BWN
printf("%p\n",&a);//00007ff6e831a000
printf("%p\n",&b);//000000b7ce1ff8e0//此时的b就是一个新的结构体,地址与a不同
printf("%p\n",a.name);//00007ff6e831b000
printf("%p\n",b.name);//00007ff6e831b000//
//因为BWM只有一个,所以a.name和b.name指向的是同一个地址,所以地址相同
return 0;
}
上例中,name属性变成了一个字符指针,这时a赋值给b,此时b变量仍然是新开辟的内存空间,但是,a和b的name成员保存的指针相同,也就是说两个属性共享一个BWM
在C语言中,相同的字符串,常量通常只会保存一份,所以那些字符串常量共享相同的内存。当你声明多个指针变量,并让他们指向相同的字符串常量时,他们实际上都指向相同的内存空间,字符串常量的共享,有利于减小程序的内存占用
注意:
C语言没有提供比较两个自定义数据结构是否相等的方法,无法用比较运算符(比如:==和!=)来比较两个数据结构是否相等或不等
结构体数组:
对比结构体和数组:


结构体数组的声明:
结构体数组:数组元素是结构体变量而构成的数组。先定义结构体类型,然后用结构体类型定义数组变量。
方式1:先声明1个结构体类型,再用此类型定义结构体数据
格式:结构体类型 数组名[数组长度];
举例:
#include <stdio.h>
struct person {
char *name;
int age;
};
//对应方式2
struct student {
int age;
char name[30];
}stu[3] = {{.age = 18,.name = "susi"},{.age = 25,.name = "sara"},{.age = 28,.name = "lusiya"}};
int main() {
//1.如何创建结构体数组
//1.1 方式1:声明结构体之后,声明结构体类型
struct person person[3];
//1.2 方式2:声明结构体的同时,创建结构体数组
//2.如何给结构体数组的成员赋值
//2.1 方式1:对应1.1的方式1
struct person per[3] = {
{.name = "ton",.age = 18},
{.name = "som",.age = 24},
{.name = "sara",.age = 26}};
//2.2 方式2:对应1.2的方式2
//看上面
//3.如何调用
//方式1:使用数组角标方式
printf("%s\n",per[2].name);
//方式2:使用指向数组或数组元素的指针(具体看下节内容)
struct person *p = &per[1];
printf("%s\n",p->name);
return 0;
}
练习1:
//输入一个班级的学生信息(包括id,name,gender,score),并把学习成绩超过全班平均成绩的学生找出,输出这部分学生的姓名和成绩
#include <stdio.h>
#define NUM 2
int i;
struct student {
int id;//学号
char name[20];
char gender;//性别
int score;
};
double total = 0,average;
int main() {
struct student stu[NUM];
for ( i = 0;i < NUM;i++){
printf("请输入学生信息\n学号: \n"
"姓名: \n"
"性别: \n"
"成绩: \n");
scanf("%d %s %c %d",&stu[i].id,&stu[i].name,&stu[i].gender,&stu[i].score);
total += stu[i].score;
}
average = total / NUM;
for (int j =0;j < NUM;j++) {
if (stu[j].score > average) {
printf("%s %d\n",stu[j].name,stu[j].score);
}
}
return 0;
}
练习2:记录投票
#include <stdio.h>
#include <string.h>
#define N 10
struct Person {
char name[30];
int count;
}perarr[3] = {{.name = "zhang3",.count = 0},{"li4",0},{"wang5",0}};
char vote_name[30];//记录投票给谁
int main() {
for (int i = 0; i < N; i++) {
printf("你要给谁投票:(zhang3,li4,wang5)\n");
scanf("%s",&vote_name);
for (int j = 0;j < 3;j++) {
if (strcmp(vote_name,perarr[j].name)==0) {
perarr[j].count++;
}
}
}
for (int i =0;i < 3;i++) {
printf("%s %d\n",
perarr[i].name,perarr[i].count);
}
return 0;
}
补充一个知识点:
在C语言中,strcmp 函数用于比较两个字符串。这个函数的原型如下:
int strcmp(const char *s1, const char *s2);
s1和s2是要比较的两个字符串。- 如果
s1和s2相同,strcmp返回 0。 - 如果
s1小于s2(即s1在字典序上先于s2),strcmp返回一个负数。 - 如果
s1大于s2(即s1在字典序上后于s2),strcmp返回一个正数。
因此,在条件判断语句中使用 == 0 是为了检查两个字符串是否完全相同。例如:
if (strcmp(vote_name, "zhang3") == 0) {
// 如果 vote_name 等于 "zhang3",则执行这里的代码
}
在这个例子中,strcmp(vote_name, "zhang3") == 0 意味着 vote_name 字符串与 "zhang3" 完全相同。如果它们相同,则返回 0,条件为真,进入 if 语句块执行相应的代码。
总结一下,== 0 的意思是检查 strcmp 函数的结果是否为 0,即两个字符串是否相等。
结构体指针:
结构体指针:指向结构体变量的指针(将结构体变量的起始地址存放在指针变量中)
具体应用场景:1.可以指向单一的结构体变量
2.可以用作函数的参数
3.可以指向结构体数组
定义结构体指针变量的格式:
struct 结构体名 *结构体指针变量名;
举例:
#include <stdio.h>
struct person {
char name[20];
int age ;
}per = {.name = "wangzhu",.age = 19};
int main() {
struct person *p = &per;
printf("%s %d",(*p).name,(*p).age);
return 0;
}
注意输出里要带个括号
结构体传参:
结构体传参的方式是值传递.
举例:
#include <stdio.h>
struct student {
char *name;
int age;
char *gender;
};
void addage(struct student per) {
per.age =per.age+1;
}
void addage1(struct student *per) {
(*per).age = (*per).age+1;
}
int main() {
struct student p1 ={.name = "TOM",.age = 18,.gender = "male"};
addage(p1);
printf("%d\n",p1.age);//输出还是为18
struct student p2 ={.name = "TOM",.age = 18,.gender = "male"};
addage1(&p2);
printf("%d",p2.age);//此时的输出为19,因为是结构体指针,可以直接修改本体
return 0;
}
练习:(方式1)
#include <stdio.h>
//1.编写一个dog结构体,包含name(char[10],age[int],weight[double]属性)
struct Dog {
char name[10];
int age;
double weight;
};
//2.编写一个函数say,返回字符串,字符串返回信息中包含所有成员值
char *say(struct Dog dog) {
static char info[100];//使用static确保info在say函数结束后不会被销毁
//引入一个新的方法,通过sprintf可以把所有信息都存入info里面
sprintf(info,"name:%s,age:%d,weight:%.2lf",dog.name,dog.age,dog.weight);
return info;
}
//在main函数中,创建Dog结构体变量,调用say函数,将调用结果打出
int main() {
struct Dog dog = {.name = "Tom",.age = 18,.weight = 121};
//或者:strcpy(dog.name,"Tom");给数组赋值
say(dog);
printf("%s",say(dog));
return 0;
}
方式2:用结构体指针
//结构体指针练习
#include <stdio.h>
//1.编写一个dog结构体,包含name(char[10],age[int],weight[double]属性)
struct Dog {
char name[10];
int age;
double weight;
};
//2.编写一个函数say,返回字符串,字符串返回信息中包含所有成员值
char *say(struct Dog *dog) {
static char info[100];//使用static确保info在say函数结束后不会被销毁
//引入一个新的方法,通过sprintf可以把所有信息都存入info里面
sprintf(info,"name:%s,age:%d,weight:%.2lf",(*dog).name,(*dog).age,(*dog).weight);
//(*dog).weight = 232;
return info;
}
//在main函数中,创建Dog结构体变量,调用say函数,将调用结果打出
int main() {
struct Dog dog = {.name = "Tom",.age = 18,.weight = 121};
//或者:strcpy(dog.name,"Tom");给数组赋值
say(&dog);
printf("%s",say(&dog));
return 0;
}
推荐使用方式2,因为这样可以直接修改要输出的东西。方式1只是复制了一份原有的。
练习2:方法1
#include <stdio.h>
#define N 3
//1.编写游客结构体,包含姓名,年龄,应付门票
struct victory {
char name[50];
int age;
int ticket;
}vic[N];
//2.编写函数ticket(),根据年龄段决定能够购买的门票的价格并输出。年龄 >= 18,门票为20,其他情况免费
int ticket(struct victory vic) {
if (vic.age >= 18) {
vic.ticket = 20;
}else{vic.ticket = 0;}
return vic.ticket;
}
//3可以循环从控制台输入名字和年龄,打印门票的收费情况,如果名字输入n,则退出程序
int main() {
for (int i = 0;i < N;i++ ) {
printf("请输入游客信息:(name,age)\n");
char info[100];
scanf("%s %d",&vic[i].name,&vic[i].age);
vic[i].ticket = ticket(vic[i]);
sprintf(info,"name:%s age:%d ticket:%d",vic[i].name,vic[i].age,vic[i].ticket);
printf("%s\n",info);
if (vic[i].name[0] == 'n') {
break;
}
}
return 0;
}
方法2:使用结构体指针
#include <stdio.h>
#define N 3
//1.编写游客结构体,包含姓名,年龄,应付门票
struct victory {
char name[50];
int age;
int ticket;
}vic[N];
//2.编写函数ticket(),根据年龄段决定能够购买的门票的价格并输出。年龄 >= 18,门票为20,其他情况免费
int ticket(struct victory *vic) {
if ((*vic).age >= 18) {
(*vic).ticket = 20;
}else{(*vic).ticket = 0;}
return (*vic).ticket;
}
//3可以循环从控制台输入名字和年龄,打印门票的收费情况,如果名字输入n,则退出程序
int main() {
for (int i = 0;i < N;i++ ) {
printf("请输入游客信息:(name,age)\n");
char info[100];
scanf("%s %d",&vic[i].name,&vic[i].age);
vic[i].ticket = ticket(&vic[i]);
sprintf(info,"name:%s age:%d ticket:%d",vic[i].name,vic[i].age,vic[i].ticket);
printf("%s\n",info);
if (vic[i].name[0] == 'n') {
break;
}
}
return 0;
}
操作符:->
举例:
struct student {
char name[20];
int age;
char gender;
};
int main() {
//打印结构体信息
struct student s = {"wangzhu", 18, 'm'};
//方式1,为结构成员访问操作符
printf("%s %d %c", s.name, s.age, s.gender);
//方式2:
struct student *p = &s;
printf("%s %d %c", (*p).name, (*p).age, (*p).gender);
//方式3:方式3 简洁一点
printf("%s %d %c", p->name, p->age, p->gender);
return 0;
}
总结:

指向结构体数组的指针:
举例:
struct person {
int id;
char name[20];
};
int main() {
struct person p1;
struct person arr[10];
struct person *p,*q;
p = &p1;
q = arr;
return 0;
}
举例:
#include <stdio.h>
struct student {
int id;
char name[20];
char gender;
}stu[3] = {
{1001,"Tom",'M'},
{1002,"Jerry",'M'},
{1003,"sara",'F'}
};
int main() {
//方式1
for (int i = 0; i < 3; i++) {
printf("%d %s %c\n",stu[i].id,stu[i].name,stu[i].gender);
}
//方式2:
struct student *p =stu;//将stu数组的首地址赋值给指针变量
for (int i = 0; i < 3; i++) {
printf("%d %s %c\n",p[i].id,p[i].name,p[i].gender);
//方式3:
printf("%d %s %c\n",p->id,p->name,p->gender);
}
return 0;
}
结构体在数据结构中的应用:
声明结点的结构体:
链表是一种动态的数据存储结构(并非固定长度),链表的基本单位是结点(node),同一链表的所有结点具有相同的数据结构,而结点使用结构体类型进行定义
一个链表节点包括数据域和指针域:数据域存储需要处理的数据,指针域存储下一个结点的位置
单链表结构的结点定义如下:
struct Node {
int data;
struct Node *next;
};
或者:
typedef struct Node {
int data;
struct Node *next;
}Lnode;
声明二叉树的结点:结构体
struct BTNode {
int data;
struct BTNode *left;
struct BTNode *right;
};
或者
typedef struct Node {
int data;
struct Node *next;
}Lnode;
声明结点变量:
方式1:
//声明单链表节点
struct Node node3;
LNode node2;
//声明二叉树结点:
struct BTNode node3;
BTNode1 node4;
方式2:就用这种方式
BTNode *node5;
node5 = (BTNode *)malloc(sizeof(BTNode));
malloc模板:当要制作一个新结点时,只要把结点结构型的名称填入括号中的类型即可
类型 *p;
p = (类型 *)malloc(sizeof(类型));//将右边创建的结点的地址赋值给p
1043

被折叠的 条评论
为什么被折叠?



