C语言(五)-结构体

C语言(五)-结构体

1. 结构体定义

在编程的时候需要将不同的类型的数据组合成为一个整体, 以便于引用。例如,一名学生有学号、姓名、性别、年龄、地址等属性,如果针对学生的学号、姓名、年龄等都单独定义一个变量,那么在有多名学生时,变量就难以分清。为此,C语言提供结构体来管理不同类型的数据组合。

语法:

struct 结构体名 { 结构体成员列表 };

示例:

// 结构体定义;
struct student{
    char name[20];
    int age;
};  // 结构体类型声明, 注意最后一定要加分号;

通过结构体创建变量的方式有三种:

struct 结构体名 变量名;
struct 结构体名 变量名 = {成员1值,成员2值};
// 定义结构体时顺便创建变量, 定义语句后的变量名;
struct 结构体名 {} 变量名;
  • 方式一

    该方式使用较少,主要使用的是方式二;

    void test02(){
        struct student s2 ;
        s2.id = 2;
        s2.score = 90.0;
        s2.age = 18;
    //    s2.name[64] = "Jason";  数组赋值出错;
        // 这里可以使用 strcpy 进行赋值,在方式三种查看
    
        printf("id=%d\n", s2.id);
    //    printf("name=%s\n", s2.name);
        printf("score=%f\n", s2.score);
        printf("age=%d\n", s2.age);
    }
    
    int main() {
        print_msg();
        test02();
        return 0;
    }
    
    

    image-20240809172304602

  • 方式二

    void test01(){
        // 通过结构体类型, 创建结构体变量
        // 给结构体变量的时候需要按照顺序赋值;
        struct student s1 = {1, "Tom", 99.9, 18};
        printf("id=%d\n", s1.id);
        printf("name=%s\n", s1.name);
        printf("score=%f\n", s1.score);
        printf("age=%d\n", s1.age);
    }
    
    int main() {
        print_msg();
        test01();
        return 0;
    }
    

    image-20240809170403181

  • 方式三

    #include <string.h>
    
    // 在定义结构体时候, 顺便创建结构体变量
    struct student2 {
        int id;			//学号
        char name[64];  //姓名
        float score;	//分数
        int age;		//年龄
    }s3;  // s3 代表通过 struct student2 创建的结构体变量;
    
    void test03(){
        s3.id = 2;
    //    s3.name = "Jerry";  //error
        strcpy(s3.name, "Jerry");  // 使用 strcpy 赋值;
        s3.score = 80;
        s3.age = 19;
    
        printf("id=%d\n", s3.id);
        printf("name=%s\n", s3.name);
        printf("score=%f\n", s3.score);
        printf("age=%d\n", s3.age);
    }
    
    int main() {
        print_msg();
        test03();
        return 0;
    }
    
    

    image-20240809173312755

总结:

结构体类型声明要放在 main 函数之前,这样 main 函数中才可以使用这个结构体,工作中往往把结构体声明放在头文件中。注意,结构体类型声明最后一定要加分号,否则会编译不通。另外,定义结构体变量时,使用struct student来定义,不能只有structstudent,否则也会编译不通。

结构体的初始化只能在一开始定义,

// 已经定义
struct student s2;
// 不能够再进行结构体的的整体赋值
s2 = {1, "Jason", 60, 18}// error

如果结构体变量已经定义,那么只能对它的每个成员单独赋值,如:

s2.age = 18;  // 单独赋值;

总结1:定义结构体时的关键字是struct,不可省略

总结2:创建结构体变量时,关键字 struct 可以省略

总结3:结构体变量利用操作符 . 访问成员

2. 结构体数组

作用:将自定义的结构体放入到数组中方便维护

语法:

struct  结构体名 数组名[元素个数] = {  {} , {} , ... {} }

示例代码:

struct hero {
    int id;            //id号
    char name[64];  //姓名
    int height;        //身高
    int age;        //年龄
};

void test04() {
    struct hero arr[5] = {
            {1, "刘备", 160, 30},
            {2, "张飞", 180, 31},
            {3, "关羽", 170, 32},
            {4, "赵云", 188, 34},
            {5, "吕布", 196, 33}  //最后一行的 ',' 可以省略
    };
    // 计算数组的长度;
    int num = sizeof(arr) / sizeof(struct hero);
    for (int i = 0; i < num; i++) {
        printf("id = %d  name = %s height = %d age = %d\n", arr[i].id, arr[i].name, arr[i].height, arr[i].age);
    }
    //求年龄的平均值
    int sum = 0;
    for (int i = 0; i < num; i++) {
        sum += arr[i].age;
    }
    printf("age mean :%d\n", sum / num);

}

int main() {
    print_msg();
    test04();
    return 0;
}

image-20240809180005786

3. 结构体对齐

结构体本身的对齐规则有好几条,比较难记,主要有一条:结构体的大小必须是其最大成员数的整数倍!

struct student_type1{
    double score;
    short age;
};

struct student_type2{
    double score;
    int height;
    short age;
};

struct student_type3{
    int height;
    char sex;
    short age;
};

void test05(){
    struct student_type1 s1;
    struct student_type2 s2;
    struct student_type3 s3;

    printf("s1 size=%d\n", sizeof(s1));
    printf("s2 size=%d\n", sizeof(s2));
    printf("s3 size=%d\n", sizeof(s3));

}

int main() {
    print_msg();
    test05();
    return 0;
}

image-20240809181328022

4. 结构体指针

一个结构体变量的指针就是该变量所占据的内存段的起始地址。可以设置一个指针变量,用它指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。指针变量也可以用来指向结构体数组中的元素,从而能够通过结构体指针快速访问结构体内的每个成员。

作用:通过指针访问结构体中的成员

  • 利用操作符 -> 可以通过结构体指针访问结构体属性
struct student3{
    int id;
    char name[64];
    float score;
};

void test06(){
    // 定义结构体变量
    struct student3 s1 = { 1, "Tom", 100 };
    // 定义结构体变量的指针;
    struct student* p = &s1;
    printf("id =%d name = %s score =%.2f\n", p->id,p->name,p->score);
    printf("id =%d name = %s score =%.2f\n", s1.id,s1.name,s1.score);
    printf("id = %d name = %s score = %.2f\n", (&s1)->id, (&s1)->name, (&s1)->score);
    printf("id = %d name = %s score = %.2f\n", (*p).id, (*p).name, (*p).score);
}

int main() {
    print_msg();
    test06();
    return 0;
}

image-20240809182515189

总结:结构体指针可以通过 -> 操作符 来访问结构体中的成员

5. 结构体嵌套结构体

作用: 结构体中的成员可以是另一个结构体

例如:每个老师辅导一个学员,一个老师的结构体中,记录一个学生的结构体

示例:

//学生结构体定义
struct student4
{
    //成员列表
    char name[64];  //姓名
    int age;      //年龄
    int score;    //分数
};

//教师结构体定义
struct teacher
{
    //成员列表
    int id; //职工编号
    char name[64];  //教师姓名
    int age;   //教师年龄
    // 结构体嵌套;
    struct student4 stu; //子结构体 学生
};

void test07() {
    // 定义结构体变量;
    struct teacher t1;
    t1.id = 10000;
    strcpy(t1.name, "老王");
    t1.age = 40;
    // 为嵌套结构体赋值;
    strcpy(t1.stu.name, "张三");
    t1.stu.age = 18;
    t1.stu.score = 100;

    printf("教师的职工编号: %d 姓名:%s 年龄:%d\n", t1.id, t1.name, t1.age);
    printf("辅导学员的姓名:%s 年龄: %d 分数:%d\n", t1.stu.name, t1.stu.age, t1.stu.score);


    system("pause");

}

int main() {
    print_msg();
    test07();
    return 0;

image-20240809183523057

代码输出中包含中文字符,因此使用 VS 进行运行代码;

6. typedef

前面定义结构体变量时使用的语句是struct student s,以这种方式来定义结构体变量有些麻烦,即每次都需要写struct student,那么有没有简单一些的定义方式呢?答案是肯定的,可以选择使用typedef声明新的类型名来代替已有的类型名。

typedef struct student5 {
    int num;
    char name[20];
    char sex;
} stu, *p_stu;
typedef int INTEGER;  // 重命名;

void test08(){
    // 省去了 struct student5
    stu s = {1001, "wangle", 'M'};
    p_stu p;  // 省去了 struct student5 *
    INTEGER i =10;
    p=&s;  // 为指针赋值;
    printf("i=%d, p->num=%d", i, p->num);
}
int main() {
    print_msg();
    test08();
    return 0;
}

image-20240809184815511

使用stu定义结构体变量和使用struct student5定义结构体变量时等价的;使用INTEGER定义变量 i 和使用 int 定义变量 i 是等价的;pstu 等价于 struct student*,所以 p 是结构体指针变量。

7. 结构体函数参数

作用:将结构体作为参数向函数中传递;

传递方式有两种:

  • 值传递
  • 地址传递

示例:

//
// Created by 86152 on 2024/8/9.
//
//学生结构体定义

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

struct student {
    //成员列表
    char name[64];  //姓名
    int age;      //年龄
    int score;    //分数
};

void print_student(struct student stu) {
    stu.age = 28;
    printf("子函数中 姓名:%s 年龄: %d 分数: %d\n", stu.name, stu.age, stu.score);
}

// 使用指针, 传递地址;
void print_student2(struct student *stu) {
    stu->age = 28;
    printf("子函数中 姓名:%s 年龄: %d 分数: %d\n", stu->name, stu->age, stu->score);
}

void test09() {
    struct student stu = {"Tom", 18, 100};
    //值传递
    print_student(stu);
    printf("主函数中 姓名:%s 年龄: %d 分数: %d\n", stu.name, stu.age, stu.score);

    printf("------------------------------\n");

    //地址传递
    print_student2(&stu);
    printf("主函数中 姓名: %s 年龄: %d 分数: %d\n", stu.name, stu.age, stu.score);

    system("pause");
}

int main() {
    test09();
    return 0;
}

image-20240809191311534

8. 结构体中 const

作用:用const来防止误操作;

示例:

//
// Created by 86152 on 2024/8/9.
//

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

//学生结构体定义
struct student {
    //成员列表
    char name[64];  //姓名
    int age;      //年龄
    int score;    //分数
};

//const使用场景
void printStudent(const struct student *stu) //加const防止函数体中的误操作
{
    // stu->age = 100; //操作失败,因为加了const修饰
    printf("姓名:%s 年龄:%d 分数: %d\n", stu->name, stu->age, stu->score);

}

int main() {

    struct student stu = {"Tom", 18, 100};

    printStudent(&stu);

    system("pause");

    return 0;
}

image-20240809192028932

9. 结构体案例

案例描述:

设计一个英雄的结构体,包括成员姓名,年龄,性别;创建结构体数组,数组中存放5名英雄。

通过冒泡排序的算法,将数组中的英雄按照年龄进行升序排序,最终打印排序后的结果。

五名英雄信息如下:

{"刘备",23,"男"},
{"关羽",22,"男"},
{"张飞",20,"男"},
{"赵云",21,"男"},
{"貂蝉",19,"女"},

示例

#include "func.h"

// 创建英雄结构体;
struct hero
{
    char name[64];
    int age;
    char sex[32];
};

void bubble_sort_hero(struct hero arr[], int len) {
    // 设置英雄冒泡的算法;

    // 循环数组
    for (int i = 0; i < len-1; i++) {
        for (int j = 0; j < len - 1; j++) {
            if (arr[j].age > arr[j + 1].age) {
                struct hero temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

void print_hero_arr(const struct hero arr[], int len) {
    // 定义打印英雄的数组;
    for (int i = 0; i < len - 1; i++) {
        printf("姓名:%s 年龄:%d 性别:%s\n", arr[i].name, arr[i].age, arr[i].sex);
    }
}

int main() {
    print_msg();
    struct hero arr[5] =
    {
        {"刘备",23,"男"},
        {"关羽",22,"男"},
        {"张飞",20,"男"},
        {"赵云",21,"男"},
        {"貂蝉",19,"女"},
    };
    int arr_len = sizeof(arr) / sizeof(struct hero);
    bubble_sort_hero(arr,arr_len);
    print_hero_arr(arr, arr_len);
    return 0;
}

image-20240809212535889

存在中文,使用 VS 进行编程;

度过大难,终有大成!

继续努力,终成大器!

posted @ 2024-08-09 21:33  紫青宝剑  阅读(43)  评论(0编辑  收藏  举报