Skip to content

结构体

约 1331 个字 501 行代码 预计阅读时间 11 分钟

结构

结构式C语言中一种用户自定义的可用的数据类型,允许存储不同的数据项

结构体中的数据成员可以是基本数据结构,也可以是其他结构体类型,甚至是指针类型

声明结构体:struct 结构体名 { 数据成员声明; }

结构体的声明由关键词struct和结构体的名称组成,结构体名称是自行定义的

并且结构体的声明可以在main函数之外,可以作为全局的一个变量

当然,也可以在main里面定义,但是出了函数之后就不能使用了

C
1
2
3
4
5
struct student {
    char name[20];
    int age;
    float score;
}student1, student2;

这个student是结构体标签,表示定义了一种新的结构体数据结构,其中这种数据结构中包括了三个变量,分别是nameagescore

然后我们在定义完结构体之后,在}后面;之前声明了我们想要的结构体变量,这里我们声明了两个student结构体变量student1student2

一般情况下,结构体标签,内部变量的声明和结构体变量的声明三个至少出现两个

其他写法

  1. 没有直接声明变量
C
//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//结构体的标签被命名为SIMPLE,没有声明变量
struct SIMPLE
{
    int a;
    char b;
    double c;
};
//用SIMPLE标签的结构体,另外声明了变量t1、t2、t3
struct SIMPLE t1, t2[20], *t3;
  1. 没有标签
C
1
2
3
4
5
6
7
8
9
//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//同时又声明了结构体变量s1
//这个结构体并没有标明其标签
struct
{
    int a;
    char b;
    double c;
} s1;
  1. 使用typedef创造新类型
C
1
2
3
4
5
6
7
8
9
//也可以用typedef创建新类型
typedef struct
{
    int a;
    char b;
    double c;
} Simple2;
//现在可以用Simple2作为类型声明新的结构体变量
Simple2 u1, u2[20], *u3;

不直接声明标签的结构体即使内部成员和声明了标签的另外一个结构体完全一样,编译器也会把这两个当做是不同的类型

结构体的成员也可以包含其他结构体,特可以包含指向自己结构体类型的指针,而通常这种指针的应用是为了更加高级的数据结构比如链表和树

C
1
2
3
4
5
6
//此结构体的声明包含了指向自己类型的指针
struct NODE
{
    char string[100];
    struct NODE *next_node;
};

我们暂时不考虑这些互相包含的结构体

结构体的初始化

C
    #include <stdio.h>

    struct Books
    {
    char  title[50];
    char  author[50];
    char  subject[100];
    int   book_id;
    } book = {"C 语言", "C", "编程语言", 123456};

    int main()
    {
        printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id);
    }

也可以直接赋值,通过我们最熟悉的scanf函数来输入结构体变量的值

Bash
7-1 计算职工工资
分数 15
作者 C课程组
单位 浙江大学
给定N个职员的信息,包括姓名、基本工资、浮动工资和支出,

要求编写程序顺序输出每位职员的姓名和实发工资(实发工资=基本工资+浮动工资-支出)。

输入格式:
输入在一行中给出正整数N。随后N行,每行给出一位职员的信息,格式为

“姓名 基本工资 浮动工资 支出”,中间以空格分隔。

其中“姓名”为长度小于10的不包含空白字符的非空字符串,其他输入、输出保证在单精度范围内。

输出格式:
按照输入顺序,每行输出一位职员的姓名和实发工资,间隔一个空格,工资保留2位小数。

输入样例:
3
zhao 240 400 75
qian 360 120 50
zhou 560 150 80
输出样例:
zhao 565.00
qian 430.00
zhou 630.00
C
#include<stdio.h>
struct inf{
    char s[10];
    double mon;
    double temp;
    double pay;
};
int main(){
    int n;
    scanf("%d\n",&n);
    struct inf name[n];
    for(int i = 0;i < n;i++){
        scanf("%s %lf %lf %lf\n",name[i].s,&name[i].mon,&name[i].temp,&name[i].pay);
    }
    for(int i = 0;i < n;i++){
        double sum = name[i].mon+name[i].temp-name[i].pay;
        printf("%s %.2f\n",name[i].s,sum);
    }
    return 0;
}

结构体的访问

成员访问运算符.

C
#include <stdio.h>
#include <string.h>

struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
};

int main( )
{
   struct Books Book1;        /* 声明 Book1,类型为 Books */
   struct Books Book2;        /* 声明 Book2,类型为 Books */

   /* Book1 详述 */
   strcpy( Book1.title, "C Programming");
   strcpy( Book1.author, "Nuha Ali"); 
   strcpy( Book1.subject, "C Programming Tutorial");
   Book1.book_id = 6495407;

   /* Book2 详述 */
   strcpy( Book2.title, "Telecom Billing");
   strcpy( Book2.author, "Zara Ali");
   strcpy( Book2.subject, "Telecom Billing Tutorial");
   Book2.book_id = 6495700;

   /* 输出 Book1 信息 */
   printf( "Book 1 title : %s\n", Book1.title);
   printf( "Book 1 author : %s\n", Book1.author);
   printf( "Book 1 subject : %s\n", Book1.subject);
   printf( "Book 1 book_id : %d\n", Book1.book_id);

   /* 输出 Book2 信息 */
   printf( "Book 2 title : %s\n", Book2.title);
   printf( "Book 2 author : %s\n", Book2.author);
   printf( "Book 2 subject : %s\n", Book2.subject);
   printf( "Book 2 book_id : %d\n", Book2.book_id);

   return 0;
}

结构体.成员的方式访问结构体的成员,直接调用了变量,可以进行复制,取值等操作,和别的变量没有其他区别

作为函数参数被传入传出

C
#include <stdio.h>
#include <string.h>

struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
};

/* 函数声明 */
void printBook( struct Books book );
int main( )
{
   struct Books Book1;        /* 声明 Book1,类型为 Books */
   struct Books Book2;        /* 声明 Book2,类型为 Books */

   /* Book1 详述 */
   strcpy( Book1.title, "C Programming");
   strcpy( Book1.author, "Nuha Ali"); 
   strcpy( Book1.subject, "C Programming Tutorial");
   Book1.book_id = 6495407;

   /* Book2 详述 */
   strcpy( Book2.title, "Telecom Billing");
   strcpy( Book2.author, "Zara Ali");
   strcpy( Book2.subject, "Telecom Billing Tutorial");
   Book2.book_id = 6495700;

   /* 输出 Book1 信息 */
   printBook( Book1 );

   /* 输出 Book2 信息 */
   printBook( Book2 );

   return 0;
}
void printBook( struct Books book )
{
   printf( "Book title : %s\n", book.title);
   printf( "Book author : %s\n", book.author);
   printf( "Book subject : %s\n", book.subject);
   printf( "Book book_id : %d\n", book.book_id);
}

传入的可以是结构体变量,也可以把内部成员作为变量传出去.

结构体指针

定义一个指向结构的指针,方式与定义指向其他类型变量的指针类似

C
struct Books *ptr;

可以在上面的变量中存储Books型结构变量的地址

C
ptr = &Book1;

这样ptr就指向Book1结构变量的地址

为了使用指向该结构的指针访问结构的成员,必须使用->运算符

C
ptr->title = "C 语言";

这样就把Book1title成员的值改为C 语言

结构体大小的计算

sizeof运算符可以计算结构体的大小,返回的事结构体的总字节数,包括所有成员变量的大小以及可能的填充字节

C
#include <stdio.h>

struct Person {
    char name[20];
    int age;
    float height;
};

int main() {
    struct Person person;
    printf("结构体 Person 大小为: %zu 字节\n", sizeof(person));
    return 0;
}

输出:

Bash
结构体 Person 大小为: 28 字节

注意

结构体的大小可能会受到编译器的优化和对齐规则的影响,编译器可能会在结构体中插入一些额外的填充字节以对齐结构体的成员变量,以提高内存访问效率。因此,结构体的实际大小可能会大于成员变量大小的总和

结构体数组

Tips

结构体也可以定义为数组,数组中每个元素都是一个结构体变量

C
struct Books books[10];

这样books数组中有10个Books结构体变量

数组的下标从0开始,可以用books[i]的方式访问数组中的元素

C
1
2
3
4
books[0].title = "C 语言";
books[0].author = "C";
books[0].subject = "编程语言";
books[0].book_id = 123456;

这样就把books[0]title成员的值改为C 语言

HW9好题

Bash
7-9 值班安排
分数 50
作者 张泳
单位 浙大城市学院
医院有A、B、C、D、E、F、G 7位大夫,在一星期内(星期一至星期天)每人要轮流值班一天,如果已知:
(1)A大夫比C大夫晚1天值班;
(2)D大夫比E大夫晚1天值班;
(3)E大夫比B大夫早2天值班
(4)B大夫比G大夫早4天值班;
(5)F大夫比B大夫晚1天值班;
(6)F大夫比C大夫早1天值班;
(7)F大夫星期四值班。
就可以确定周一至周日的值班人员分别为:E、D、B、F、C、A、G。
编写程序,根据输入的条件,输出星期一至星期天的值班人员。

输入格式:
先输入一个整数n,再输入n组条件,要求能够根据输入的条件确定唯一的值班表,且输入的n组条件中能够直接或间接得到任意两位大夫的关联关系,例如上面的条件(2)直接显示了D与E间的关系,而通过条件(1)、(6)、(5)可以间接得到A与B的关系。
条件的输入格式有2种:
格式1:编号 比较运算符 编号 天数
其中比较运算符有2种:>  < ,分别表示“早”或“晚”
例如:A<C1  表示:A大夫比C大夫晚1天值班
格式2:编号 = 数值
例如:F=4  表示:F大夫在星期四值班

输出格式:
输出周一至周日的值班序列。

输入样例:
7
A<C1
D<E1
E>B2
B>G4
F<B1
F>C1
F=4
输出样例:
EDBFCAG
C
#include<stdio.h>
struct condition{
    int obj1;//用于存储输入的第一个字母- 'A'的值 
    int obj2;//存储第二个字母的 
    int num;//存储数字 
    int ret;//是否是 =  
}condition[20];

int judge(int *a,int n);    //判断函数 
int main(void)
{
    int n;
    scanf("%d\n",&n);
    int i;
    char s[5];

    //录入数据 
    for(i=0; i<n; i++){
        gets(s);
        condition[i].obj1 = s[0] - 'A';
        if(s[1] != '='){
            condition[i].obj2 = s[2] - 'A';
            condition[i].num = s[3] - '0';
            condition[i].ret = 0;       //如果输入的不是 = 就ret录为1;        
        }
        else{
            condition[i].num = s[2] - '0';  
            condition[i].ret = 1;
        }
        if(s[1] == '<'){
            condition[i].num *= -1;     //如果是 < 存储的数字变为负; 
        }

    }

    //循环给数组赋值 0~6各种情况,在给判断函数判断是否符合输入的条件。 
    int day[7] ;    //值班顺序 ,下标表示 ABCDEFG,对应值表示时间。 
    int a,b,c,d,e,f,g;
    for(a=0; a<7; a++){
        for(b=0; b<7 ; b++){
            for(c=0; c<7 ; c++){
                for(d=0; d<7 ; d++){
                    for(e=0; e<7 ; e++){
                        for(f=0; f<7 ; f++){
                            for(g=0; g<7 ; g++){
                                day[0] = a;
                                day[1] = b;
                                day[2] = c;
                                day[3] = d;
                                day[4] = e;
                                day[5] = f;
                                day[6] = g;
                                if(judge(day,n))    //判断是否符合输入的条件。 
                                    goto E;
                            }
                        }
                    }
                }
            }
        }
    }
E:  
    if(judge(day,n)){
        char Day[8];
        for(i=0; i<7; i++){
            Day[day[i]] = i + 'A';  //把值班时间按顺序写入对应大夫 
        }
        Day[8] = '\0';
        puts(Day);
    }

    return 0;
}

int judge(int *a,int n){
    int i = 0;
    int ret = 1;

    //判断传入的值班日期与输入的条件是否符合。 
    for(i=0; i<n; i++)
    {
        //如果是 = ,就判断值班日期和条件日期-1是否相等 .(值班日期是从0开始算的) 
        if(condition[i].ret){
            if(a[condition[i].obj1] != condition[i].num - 1)
                ret = 0;
                break;
        } 
        else if(a[condition[i].obj1] + condition[i].num != a[condition[i].obj2]){
            ret = 0;
            break;
        }
    }
    return ret;
}
Bash
7-8 停车场管理
分数 50
作者 张泳
单位 浙大城市学院
设有一个可以停放n辆汽车的狭长停车场,它只有一个大门可以供车辆进出。车辆按到达停车场时间的先后次序依次从停车场最里面向大门口处停放 (即最先到达的第一辆车停放在停车场的最里面) 。如果停车场已放满n辆车,则以后到达的车辆只能在停车场大门外的便道上等待,一旦停车场内有车开走,则排在便道上的第一辆车可以进入停车场。停车场内如有某辆车要开走,则在它之后进入停车场的车都必须先退出停车场为它让路,待其开出停车场后,这些车辆再依原来的次序进场。每辆车在离开停车场时,都应根据它在停车场内停留的时间长短交费,停留在便道上的车不收停车费。编写程序对该停车场进行管理。

输入格式:
先输入一个整数n(n<=10),再输入若干组数据,每组数据包括三个数据项:汽车到达或离开的信息(A表示到达、D表示离开、E表示结束)、汽车号码、汽车到达或离开的时刻。

输出格式:
若有车辆到达,则输出该汽车的停车位置;若有车辆离开,则输出该汽车在停车场内停留的时间。如果汔车号码不存在,输出the car not in park

输入样例:
3
A 1 1 
A 2 2
A 3 3
D 1 4
A 4 5
A 5 6
D 4 7
D 5 8
E 0 0
输出样例:
car#1 in parking space #1
car#2 in parking space #2
car#3 in parking space #3
car#1 out,parking time 3
car#4 in parking space #3
car#5 waiting
car#4 out,parking time 2
car#5 in parking space #3
car#5 out,parking time 1
C
#include <stdio.h>

typedef struct CAR{
    char zt;
    int num;
    int in;
}car;

int main()
{
    int n;
    scanf("%d",&n);
    int space=1,xb;
    car a[100];
    int last,wait=-1,k=0,flag,f;

    do{
        scanf("%c %d %d",&a[k].zt,&a[k].num,&a[k].in);
        if(a[k].zt!='E')k++;
    }while(a[k].zt!='E');

    for(int i=0;i<k;i++){
        if(a[i].zt=='E'){
            break;
        }else if(a[i].zt=='A'){
            if(space!=n+1){
                printf("car#%d in parking space #%d\n",a[i].num,space);
                space++;
            }else{
                printf("car#%d waiting\n",a[i].num);
                wait=a[i].num;
            }
        }else if(a[i].zt=='D'){
            f=0;
            flag=a[i].in;
            for(int j=0;j<i;j++){
                if(a[i].num==a[j].num){
                    f=1;
                    last=a[i].in-a[j].in;
                    break;
                }
            }
            if(f){
            printf("car#%d out,parking time %d\n",a[i].num,last);
            //此处将离开停车场的号码设置为不作处理的号码
            for(int p=0;p<k;p++){
                if(a[p].num==a[i].num){
                    a[p].num=-1;
                }
            }

            space--;
            if(wait!=-1){
                for(int j=0;j<k;j++){
                if(wait==a[j].num&&a[j].zt=='A'){
                    a[j].in=flag;
                    break;
                }
                }
                printf("car#%d in parking space #%d\n",wait,space);
                wait=-1;
            }
            }else{
                printf("the car not in park\n");
            }   
        }
    }
}

HW10边写边整理

链表的结构

  1. header:头指针
  2. 若干节点,包括数据域和指针域,最后一个节点的指针域指向空

实现原理:头指针指向链表的第一个节点,然后第一个节点的指针指向下一个节点,然后以此指向最后一个节点,这样就形成了一个链表

链表的数据结构

C
1
2
3
4
struct Node{
    int data;
    struct Node *next;
};

本质上是一个结构体,包括数据域和指针域

然后我们通过改变data的类型就可以存储不同的数据了

创建链表

C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct list_node {
    int data;           // 数据域:存储整数数据
    struct list_node *next;  // 指向下一个节点的指针
};

int main(void)
{
    struct list_node *node = NULL ;          // 定义一个头指针
    node = (struct list_node *)malloc(sizeof(struct list_node));  // 分配内存给一个 list_node 类型的节点
    if (node == NULL) {
        printf("malloc fail!\n");  // 如果分配失败,输出错误信息
        return 1;  // 提前退出
    }

    memset(node, 0, sizeof(struct list_node)); // 初始化节点数据为0
    node->data = 100 ;                     // 给链表节点的数据赋值为 100
    node->next = NULL ;                    // 将链表的指针域指向 NULL,表示链表的尾部
    printf("%d\n", node->data);            // 输出链表节点的数据

    free(node);                            // 释放分配的内存
    return 0 ;
}

node = (list_node *)molloc(sizeof(list_node))这行代码可以看出,我们通过malloc函数申请了一块内存空间,然后将其转换为list_node类型的指针,并将其初始化为0。

其中(list_node *)是强制类型转换,将malloc函数返回的指针转换为list_node类型的指针。

这个指针指向的就是分配的那块内存空间的地址

之后我们再赋值,赋值完之后,我们再将指针域指向

然后输出print一下,最后释放掉内存空间

提炼一下分为这么几步

  1. 定义结构体来表示链表的这个结构
  2. 定义头指针
  3. 将头指针指向我们申请的内存空间
  4. 然后给我们有的内存空间赋值
  5. 最后将我们的指针指向空,等待下一个链表节点
  6. 清空我们的链表结构体,free掉内存空间
  7. return 0 ;

遍历链表

C
1
2
3
4
5
6
7
8
void traverse(){
    struct Node *temp = head;
    while(temp!= NULL){
        printf("%d ",temp->data);
        temp = temp->next;
    }
    printf("\n");
}

删除节点

C
void delete(int data){
    struct Node *temp = head;
    struct Node *prev = NULL;
    while(temp!= NULL && temp->data!= data){
        prev = temp;
        temp = temp->next;
    }
    if(temp == NULL){
        printf("Node not found\n");
    }else{
        if(prev == NULL){
            head = temp->next;
        }else{
            prev->next = temp->next;
        }
        free(temp);
    }
}