40 C 语言结构体:结构体数据类型、结构体变量、访问结构体成员、结构体指针

news2024/10/9 3:42:51

目录

1 为什么需要结构体

2 什么是结构体

3 声明结构体类型

3.1 语法格式

3.2 案例演示

3.2.1 学生信息结构体

3.2.2 通讯录条目结构体

3.2.3 猫咪结构体

4 声明结构体变量

4.1 什么是结构体变量

4.2 声明结构体变量的常见方式

5 结构体和结构体变量的区别与联系

6 访问结构体成员

6.1 结构体成员的访问方式

6.2 为结构体成员逐个赋值

6.2.1 案例演示 1:逐个赋值与修改

6.2.2 案例演示 2:初始化三种方式声明的结构体

6.3 使用大括号一次性初始化结构体成员

6.3.1 案例演示 1:初始化猫咪结构体

6.3.2 案例演示 2:初始化学生结构体

6.3.3 案例演示 3:初始化三种方式声明的结构体

7 结构体指针

7.1 按值传递结构体

7.2 声明结构体指针

7.3 通过结构体指针访问成员

7.4 结构体类型转换

8 编程练习

8.1 小狗案例

8.2 盒子案例

8.3 景区门票案例


1 为什么需要结构体

        在 C 语言中,除了几种基本的数据类型之外,数组是唯一内置的复合数据类型,能够存储多个相同类型的值。然而,由于其只能包含同类型的数据,这在实际应用中可能会带来一定的限制。指针数组虽然能够存储不同类型的数据,但对其数据进行操作不是很方便。

场景一:

        现有需求开发一个学生档案管理系统,其中需要详细记录每位学生的信息,包括学号、姓名、性别、年龄和家庭住址等内容,这些信息综合反映了学生的整体概况。

场景二:

        隔壁的老王养了两只猫咪。其中一只名为“小黄”,是一只 2 岁的橘色猫咪;另一只名为“小黑”,是一只 3 岁的黑色猫咪。请编写一个程序,该程序能够根据用户输入的猫咪名字,输出对应的猫咪名字、年龄和颜色。若用户输入的名字与老王家的猫咪名字不符,则程序应提示“老王没有这只猫”。

传统的方法可能包括以下两种尝试:

        单独定义多个变量:为每只猫咪的名字、年龄和颜色分别定义独立的变量来存储信息。这种方法虽然直接,但随着猫咪数量的增加,变量的数量也会迅速增长,导致代码难以管理和维护。

        使用数组:数组是一种用于存储一系列相同类型数据的结构。然而,在本例中,我们需要存储的是每只猫咪的不同类型的信息(如名字为字符串,年龄为整数)。由于这些信息的数据类型各不相同,因此无法将它们整合到同一个数组中,这限制了数组在这种场景下的适用性。

        为了更有效地管理这些不同类型的猫咪信息,可以考虑使用 C 语言中的结构体(struct),它可以将不同类型的数据组织在一起,形成一个易于管理的整体。这样不仅能够清晰地表示每只猫咪的信息,还方便对多只猫咪的数据进行操作和扩展。


2 什么是结构体

        C 语言通过 struct 关键字提供了一种自定义复合数据类型的能力,允许将多种不同类型的数据组合成一个单元,这种数据类型被称为结构体(structure)

        尽管 C 语言不具备其他高级语言中的对象(object)和类(class)概念,但结构体在很大程度上模拟了这些功能,使得数据的组织和管理更加高效和直观。通过结构体,我们可以轻松地创建复杂的数据模型,如学生记录、员工信息等,极大地增强了程序处理现实世界问题的能力。


3 声明结构体类型

3.1 语法格式

        在 C 语言中,构建一个结构体类型的一般语法格式如下:

struct 结构体名称 {
    数据类型1 成员名1;  // 分号分隔结构体成员
    数据类型2 成员名2;
    ...
    数据类型n 成员名n;
};  // 分号不要忘记
  • struct 是 C 语言的关键字,用于定义结构体类型
  • 结构体名称是自定义的标识符,用于命名结构体类型。
  • 成员名1 至成员名 n 是结构体内部的数据成员,每个成员都有自己的数据类型,可以是 C 语言中的任何合法数据类型,包括基本数据类型、指针类型,甚至是其他结构体类型。

3.2 案例演示

3.2.1 学生信息结构体

// 定义结构体:学生
struct Student {
    int id;          // 学号
    char name[50];   // 姓名
    char gender;     // 性别,建议使用字符型,例如 'M' 表示男性,'F' 表示女性
    int age;         // 年龄
    char address[100]; // 家庭住址
};

        在此示例中,Student 结构体包含了学号、姓名、性别、年龄和家庭住址五个成员。注意,对于姓名和家庭住址,使用了固定长度的字符数组(char name[50] 和 char address[100])来存储,而不是指针(char *name 和 char *address),这是因为直接使用字符数组可以避免内存分配和释放的问题,更适合于简单且固定的字符串存储。但在很多情况下,使用指针来定义字符串类型的成员变量比使用固定长度的字符数组更加灵活和方便。

3.2.2 通讯录条目结构体

// 定义结构体:通讯录
struct Contacts {
    char name[50];        // 姓名
    int birth_year;       // 出生年份
    int birth_month;      // 出生月份
    int birth_day;        // 出生日
    char email[100];      // 电子邮件地址
    char mobile_number[12]; // 手机号码
};

        在此示例中,Contacts 结构体设计用于存储个人的联系信息,包括姓名、出生日期、电子邮件地址和手机号码。通过将出生日期分为年、月、日三个独立的成员,可以使日期的处理更加灵活和方便。

3.2.3 猫咪结构体

// 定义结构体:猫咪
struct Cat {
    char *name;    // 名字
    int age;       // 年龄
    char *color;   // 颜色
};

        在此示例中,Cat 结构体设计用于存储猫咪的基本信息,包括名字、年龄和颜色。通过使用指针来存储名字和颜色,可以灵活地处理不同长度的字符串,使结构体更加通用和方便管理。年龄则使用整型变量直接存储,适用于表示具体的数值信息。

提示:

        选择使用固定长度的字符数组还是指针,取决于具体的应用场景和需求。

  • 如果确定字符串的长度不会超过某个上限,并且希望简化内存管理,那么使用固定长度的字符数组可能是更好的选择
  • 如果字符串长度不定或者需要频繁修改,那么使用指针会更加合适

        一般来说,对于字符串类型,常用指针来表示。


4 声明结构体变量

4.1 什么是结构体变量

        在 C 语言中,结构体是一种自定义的数据类型,用于将不同类型的数据组合在一起。结构体变量则是根据结构体类型创建的具体实例,用于存储实际的数据

4.2 声明结构体变量的常见方式

方式 1:先定义结构体,然后再创建结构体变量

// 定义结构体
struct Student {
    int id;          // 学号
    int age;         // 年龄
    char *name;      // 姓名
    char gender;     // 性别,建议使用字符型,例如 'M' 表示男性,'F' 表示女性
    char *address;   // 家庭住址
};

// 定义结构体变量
struct Student stu1, stu2;

        这种方式首先定义了一个名为 Student 的结构体类型,然后基于这个类型创建了两个结构体变量 stu1 和 stu2。这种方式适合需要多次使用同一结构体类型的情况

方式 2:在定义结构体的同时定义结构体变量

// 定义结构体的同时定义 stu1 和 stu2 两个变量
struct Student {
    int id;          // 学号
    int age;         // 年龄
    char *name;      // 姓名
    char gender;     // 性别,建议使用字符型,例如 'M' 表示男性,'F' 表示女性
    char *address;   // 家庭住址
} stu1, stu2;

        这种方式在定义结构体类型的同时,直接创建了两个结构体变量 stu1 和 stu2。这种方式简洁,减少了代码行数。这种方式适合只需要一次定义的情况

方式 3:在定义时也可以不给出结构体名

// 不给出结构体的名字
struct {
    int id;          // 学号
    int age;         // 年龄
    char *name;      // 姓名
    char gender;     // 性别,建议使用字符型,例如 'M' 表示男性,'F' 表示女性
    char *address;   // 家庭住址
} stu1, stu2;

        这种方式定义了一个匿名结构体,并直接创建了两个结构体变量 stu1 和 stu2。由于没有给结构体命名,这种方式适用于只需要创建少数几个特定结构体变量的场景


5 结构体和结构体变量的区别与联系

        结构体结构体是一种自定义的数据类型,类似于一个模板,定义了数据的格式。它描述了数据的结构,但不占用内存空间。

        结构体变量结构体变量是根据结构体类型创建的具体实例,用于存储实际的数据。每个结构体变量都会占用内存空间,存储相应的数据。

特征结构体(Struct)结构体变量(Struct Variable)
定义一种用户自定义的数据类型,定义了数据的格式和结构。根据结构体类型创建的具体实例,用于存储实际的数据。
内存占用不占用内存空间,只是一个类型定义。占用内存空间,用于存储实际的数据
作用提供了一种数据组织的方式,定义了数据的结构。存储和操作实际的数据,每个变量可以有不同的值。
生命周期存在于编译阶段,用于类型检查和编译。存在于运行阶段,用于存储和操作数据。
依赖关系无,可以独立定义。必须基于已定义的结构体类型创建
数据一致性确保所有结构体变量都遵循相同的格式,提供类型安全。遵循结构体定义的格式,确保数据的一致性和类型安全。
灵活性提供了一种通用的数据结构,可以被多个变量使用。可以创建多个实例,每个实例可以有不同的数据值。
初始化无需初始化,只是一个类型定义可以在定义时进行初始化,也可以在后续代码中动态赋值
访问和修改不能直接访问或修改,因为它只是一个类型定义可以直接访问和修改其成员的数据

6 访问结构体成员

6.1 结构体成员的访问方式

        结构体的成员是结构体的一个组成部分,可以是基本数据类型、数组、指针、结构体等。结构体的成员也称为属性。结构体和数组类似,是一组数据的集合,使用点号 . 获取单个成员【结构体变量.结构体成员】,可以进行赋值和取值。

        假设有一个结构体变量 struct_name,其成员为 member_name,访问和操作该成员的格式如下:

struct_name.member_name

6.2 为结构体成员逐个赋值

6.2.1 案例演示 1:逐个赋值与修改

        下面这个程序演示了如何使用 C 语言中的结构体来定义和操作学生信息。结构体 Student 包含了学号、年龄、姓名、性别和家庭住址等成员。程序首先定义了一个结构体类型 Student,然后创建了一个结构体变量 stu,并逐个对结构体的成员进行赋值。最后,程序输出了这些成员的值,并在输出性别时进行了相应的转换。此外,程序还展示了如何修改结构体成员的值。

#include <stdio.h>

int main()
{
    // 声明结构体
    struct Student
    {
        int id;        // 学号
        int age;       // 年龄
        char *name;    // 姓名
        char gender;   // 性别,建议使用字符型,例如 'M' 表示男性,'F' 表示女性
        char *address; // 家庭住址
    };

    // 声明结构体变量
    struct Student stu;

    // 逐个对成员进行赋值
    stu.id = 1001;
    stu.age = 19;
    stu.name = "Jack Ma";
    stu.gender = 'M';
    stu.address = "Beijing, China";

    // 获取成员的值并输出
    printf("id: %d \n", stu.id);
    printf("name: %s \n", stu.name);
    printf("age: %d \n", stu.age);
    // printf("gender: %c \n", stu.gender);
    if (stu.gender == 'M')
    {
        printf("性别: 男 \n");
    }
    else if (stu.gender == 'F')
    {
        printf("性别: 女 \n");
    }
    else
    {
        printf("性别未知\n");
    }
    printf("address: %s \n", stu.address);

    // 修改成员的值
    stu.address = "ShangHai, China";
    printf("new address: %s \n", stu.address);

    return 0;
}

        输出结果如下所示:

6.2.2 案例演示 2:初始化三种方式声明的结构体

        下面这个程序演示了在 C 语言中使用结构体来定义和操作学生信息的三种常见方式。结构体 Student 包含了学号、年龄、姓名、性别和家庭住址等成员。程序通过三种方式创建和初始化结构体变量,并输出每个变量的成员值,程序最后输出每个结构体变量的成员值,展示不同方式创建和初始化结构体变量的效果。

#include <stdio.h>

// 方式 1:先定义结构体,然后再创建结构体变量
struct Student1
{
    int id;        // 学号
    int age;       // 年龄
    char *name;    // 姓名
    char gender;   // 性别,建议使用字符型,例如 'M' 表示男性,'F' 表示女性
    char *address; // 家庭住址
};

// 方式 2:在定义结构体的同时定义结构体变量
struct Student2
{
    int id;        // 学号
    int age;       // 年龄
    char *name;    // 姓名
    char gender;   // 性别,建议使用字符型,例如 'M' 表示男性,'F' 表示女性
    char *address; // 家庭住址
} stu2_1, stu2_2;

// 方式 3:在定义时也可以不给出结构体名
struct
{
    int id;        // 学号
    int age;       // 年龄
    char *name;    // 姓名
    char gender;   // 性别,建议使用字符型,例如 'M' 表示男性,'F' 表示女性
    char *address; // 家庭住址
} stu3_1, stu3_2;

int main()
{
    // 方式 1:创建并初始化结构体变量
    struct Student1 stu1_1, stu1_2;
    
    stu1_1.id = 1;
    stu1_1.age = 20;
    stu1_1.name = "张三";
    stu1_1.gender = 'M';
    stu1_1.address = "北京";

    stu1_2.id = 2;
    stu1_2.age = 21;
    stu1_2.name = "李四";
    stu1_2.gender = 'F';
    stu1_2.address = "上海";

    // 方式 2:初始化结构体变量
    stu2_1.id = 3;
    stu2_1.age = 22;
    stu2_1.name = "王五";
    stu2_1.gender = 'M';
    stu2_1.address = "广州";

    stu2_2.id = 4;
    stu2_2.age = 23;
    stu2_2.name = "赵六";
    stu2_2.gender = 'F';
    stu2_2.address = "深圳";

    // 方式 3:初始化结构体变量
    stu3_1.id = 5;
    stu3_1.age = 24;
    stu3_1.name = "钱七";
    stu3_1.gender = 'M';
    stu3_1.address = "杭州";

    stu3_2.id = 6;
    stu3_2.age = 25;
    stu3_2.name = "孙八";
    stu3_2.gender = 'F';
    stu3_2.address = "南京";

    // 输出结构体变量的信息
    printf("方式1: 学号: %d, 姓名: %s, 年龄: %d, 性别: %c, 地址: %s\n", stu1_1.id, stu1_1.name, stu1_1.age, stu1_1.gender, stu1_1.address);
    printf("方式1: 学号: %d, 姓名: %s, 年龄: %d, 性别: %c, 地址: %s\n", stu1_2.id, stu1_2.name, stu1_2.age, stu1_2.gender, stu1_2.address);

    printf("方式2: 学号: %d, 姓名: %s, 年龄: %d, 性别: %c, 地址: %s\n", stu2_1.id, stu2_1.name, stu2_1.age, stu2_1.gender, stu2_1.address);
    printf("方式2: 学号: %d, 姓名: %s, 年龄: %d, 性别: %c, 地址: %s\n", stu2_2.id, stu2_2.name, stu2_2.age, stu2_2.gender, stu2_2.address);

    printf("方式3: 学号: %d, 姓名: %s, 年龄: %d, 性别: %c, 地址: %s\n", stu3_1.id, stu3_1.name, stu3_1.age, stu3_1.gender, stu3_1.address);
    printf("方式3: 学号: %d, 姓名: %s, 年龄: %d, 性别: %c, 地址: %s\n", stu3_2.id, stu3_2.name, stu3_2.age, stu3_2.gender, stu3_2.address);

    return 0;
}

        输出结果如下所示:

6.3 使用大括号一次性初始化结构体成员

        在 C 语言中,可以使用大括号 {} 一次性对结构体的所有成员进行初始化。这种方式简洁明了,适用于在定义结构体变量时直接初始化所有成员的场景。大括号内的值必须按照结构体成员定义的顺序提供

        使用大括号 {} 进行初始化必须在定义变量时进行。一旦变量已经定义,就不能再使用大括号 {} 进行赋值。对于已经定义的变量,需要逐个成员进行赋值。

6.3.1 案例演示 1:初始化猫咪结构体

        下面这个程序定义了一个 Cat 结构体,用于存储猫咪的名字、年龄和颜色。然后,程序创建了一个 Cat 结构体变量 my_cat,并使用大括号 {} 一次性初始化其所有成员。最后,程序通过访问结构体的成员并打印它们的值,输出猫咪的信息。

#include <stdio.h>

// 定义结构体
struct Cat
{
  char *name;  // 名字
  int age;     // 年龄
  char *color; // 颜色
};

int main()
{
  // 使用大括号一次性初始化结构体成员
  // 大括号内的值必须按照结构体成员定义的顺序提供。
  struct Cat my_cat = {"Whiskers", 3, "Gray"};

  // 访问结构体成员并打印
  printf("Name: %s\n", my_cat.name);   // Name: Whiskers
  printf("Age: %d\n", my_cat.age);     // Age: 3
  printf("Color: %s\n", my_cat.color); // Color: Gray

  return 0;
}

6.3.2 案例演示 2:初始化学生结构体

        下面这个程序演示了在 C 语言中使用结构体来定义和操作学生信息。程序定义了一个结构体类型 Student,并在定义结构体变量时使用大括号 {} 一次性初始化所有成员。程序最后输出每个结构体变量的成员值。

#include <stdio.h>

int main()
{
    // 声明结构体以及结构体变量,并给结构体成员赋值
    struct Student
    {
        int id;        // 学号
        int age;       // 年龄
        char *name;    // 姓名
        char gender;   // 性别,建议使用字符型,例如 'M' 表示男性,'F' 表示女性
        char *address; // 家庭住址
    } stu1 = {1002, 19, "Jim Liu", 'f', "Songjiang Shanghai"},
      stu2 = {1003, 16, "Tom Chen", 'm', "Baoan Shenzhen"};

    // 获取成员的值并输出
    printf("stu1:\n");
    printf("id: %d \n", stu1.id);
    printf("name: %s \n", stu1.name);
    printf("age: %d \n", stu1.age);
    printf("gender: %c \n", stu1.gender);
    printf("address: %s \n\n", stu1.address);

    printf("stu2:\n");
    printf("id: %d \n", stu2.id);
    printf("name: %s \n", stu2.name);
    printf("age: %d \n", stu2.age);
    printf("gender: %c \n", stu2.gender);
    printf("address: %s \n", stu2.address);

    return 0;
}

        输出结果如下所示:

6.3.3 案例演示 3:初始化三种方式声明的结构体

        下面这个程序演示了在 C 语言中使用结构体来定义和操作学生信息的三种常见方式。每种方式都展示了如何定义结构体、创建结构体变量并一次性初始化所有成员。程序最后输出每个结构体变量的成员值。

#include <stdio.h>

// 方式 1:先定义结构体,然后再创建结构体变量
struct Student1
{
  int id;        // 学号
  int age;       // 年龄
  char *name;    // 姓名
  char gender;   // 性别,建议使用字符型,例如 'M' 表示男性,'F' 表示女性
  char *address; // 家庭住址
};

// 方式 2:在定义结构体的同时定义结构体变量
struct Student2
{
  int id;        // 学号
  int age;       // 年龄
  char *name;    // 姓名
  char gender;   // 性别,建议使用字符型,例如 'M' 表示男性,'F' 表示女性
  char *address; // 家庭住址
} stu2_1 = {3, 22, "王五", 'M', "广州"},
  stu2_2 = {4, 23, "赵六", 'F', "深圳"};

// 方式 3:在定义时也可以不给出结构体名
struct
{
  int id;        // 学号
  int age;       // 年龄
  char *name;    // 姓名
  char gender;   // 性别,建议使用字符型,例如 'M' 表示男性,'F' 表示女性
  char *address; // 家庭住址
} stu3_1 = {5, 24, "钱七", 'M', "杭州"},
  stu3_2 = {6, 25, "孙八", 'F', "南京"};

int main()
{
  // 方式 1:创建并初始化结构体变量
  struct Student1 stu1_1 = {1, 20, "张三", 'M', "北京"},
                  stu1_2 = {2, 21, "李四", 'F', "上海"};

  // 输出结构体变量的信息
  printf("方式1:\n");
  printf("学号: %d, 姓名: %s, 年龄: %d, 性别: %c, 地址: %s\n", stu1_1.id, stu1_1.name, stu1_1.age, stu1_1.gender, stu1_1.address);
  printf("学号: %d, 姓名: %s, 年龄: %d, 性别: %c, 地址: %s\n", stu1_2.id, stu1_2.name, stu1_2.age, stu1_2.gender, stu1_2.address);

  printf("方式2:\n");
  printf("学号: %d, 姓名: %s, 年龄: %d, 性别: %c, 地址: %s\n", stu2_1.id, stu2_1.name, stu2_1.age, stu2_1.gender, stu2_1.address);
  printf("学号: %d, 姓名: %s, 年龄: %d, 性别: %c, 地址: %s\n", stu2_2.id, stu2_2.name, stu2_2.age, stu2_2.gender, stu2_2.address);

  printf("方式3:\n");
  printf("学号: %d, 姓名: %s, 年龄: %d, 性别: %c, 地址: %s\n", stu3_1.id, stu3_1.name, stu3_1.age, stu3_1.gender, stu3_1.address);
  printf("学号: %d, 姓名: %s, 年龄: %d, 性别: %c, 地址: %s\n", stu3_2.id, stu3_2.name, stu3_2.age, stu3_2.gender, stu3_2.address);

  return 0;
}

        输出结果如下所示:


7 结构体指针

7.1 按值传递结构体

        当结构体按值传递时,会创建一个结构体的副本。这意味着函数接收到的是结构体的一个完整拷贝。对于大型结构体,按值传递会涉及大量的内存复制操作,从而影响性能。

#include <stdio.h>

struct MyStruct
{
    int a;
    float b;
};

void printStruct(struct MyStruct s)
{
    printf("a: %d, b: %f\n", s.a, s.b);
    s.a = 66; // 按值传递不会修改结构体的本身数据
}

int main()
{
    struct MyStruct s = {1, 2.3};
    printStruct(s);  // 按值传递
    printf("a: %d, b: %f\n", s.a, s.b); // a: 1, b: 2.300000

    return 0;
}

        输出结果如下所示:

7.2 声明结构体指针

        结构体指针是指向结构体的指针变量,允许间接访问和操作结构体的成员。使用结构体指针可以更方便地处理结构体数据,特别是在函数参数传递和动态内存管理中非常有用。声明结构体指针的语法格式如下所示:

struct 结构体名 *结构体指针变量名;

        声明结构体指针,通常需要先定义一个结构体变量,再创建结构体指针,并将结构体变量的地址赋值给结构体指针。

// 声明结构体
struct Student {
    int id;        // 学号
    int age;       // 年龄
    char *name;    // 姓名
    char gender;   // 性别,使用字符型,例如 'M' 表示男性,'F' 表示女性
    char *address; // 家庭住址
};

// 声明结构体变量
struct Student stu;

// 声明结构体指针并初始化
struct Student *ptr = &stu;

7.3 通过结构体指针访问成员

        结构体指针可以通过 -> 操作符或解引用操作符 * 访问结构体的成员。以下是一个示例程序,展示了如何通过结构体指针访问和操作结构体的成员。

#include <stdio.h>

int main()
{
    // 定义结构体
    struct Student
    {
        char *name;  // 姓名
        int age;     // 年龄
        char gender; // 性别,使用字符型,例如 'M' 表示男性,'F' 表示女性
    };

    // 创建并初始化结构体变量
    struct Student s = {"张三", 20, 'M'};

    // 通过结构体变量访问成员
    printf("通过结构体变量访问成员:\n");
    printf("name=%s, age=%d, gender=%c\n", s.name, s.age, s.gender);

    // 声明结构体指针并初始化
    struct Student *ps = &s;

    // 通过结构体指针解引用访问成员
    printf("通过结构体指针解引用访问成员:\n");
    printf("name=%s, age=%d, gender=%c\n", (*ps).name, (*ps).age, (*ps).gender);

    // 通过结构体指针使用 -> 操作符访问成员
    printf("通过结构体指针使用 -> 操作符访问成员:\n");
    printf("name=%s, age=%d, gender=%c\n", ps->name, ps->age, ps->gender);

    // 修改方式也可以这样
    s.name = "Thanks";
    printf("s.name=%s, (*ps).name=%s,  ps->name=%s\n", s.name, (*ps).name, ps->name);
    s.age = 20;
    printf("s.age=%d, (*ps).age=%d,  ps->age=%d\n", s.age, (*ps).age, ps->age);
    s.gender = 'F';
    printf("s.gender=%c, (*ps).gender=%c,  ps->gender=%c\n", s.gender, (*ps).gender, ps->gender);

    return 0;
}

        输出结果如下所示:

7.4 结构体类型转换

        C 语言是一种强类型语言,不同类型的结构体即使成员相似或完全相同,在类型系统上也被视为不同的类型。直接进行相似类型的结构体类型转换通常是不被推荐的做法,因为这可能导致未定义行为。

#include <stdio.h>

// 方式1:先定义结构体,然后再创建结构体变量
struct Student1
{
  int id;        // 学号
  int age;       // 年龄
  char *name;    // 姓名
  char gender;   // 性别,建议使用字符型,例如 'M' 表示男性,'F' 表示女性
  char *address; // 家庭住址
};

// 方式2:在定义结构体的同时定义结构体变量
struct Student2
{
  int id;        // 学号
  int age;       // 年龄
  char *name;    // 姓名
  char gender;   // 性别,建议使用字符型,例如 'M' 表示男性,'F' 表示女性
  char *address; // 家庭住址
} stu2_1 = {3, 22, "王五", 'M', "广州"},
  stu2_2 = {4, 23, "赵六", 'F', "深圳"};

// 方式3:在定义时也可以不给出结构体名
struct
{
  int id;        // 学号
  int age;       // 年龄
  char *name;    // 姓名
  char gender;   // 性别,建议使用字符型,例如 'M' 表示男性,'F' 表示女性
  char *address; // 家庭住址
} stu3_1 = {5, 24, "钱七", 'M', "杭州"},
  stu3_2 = {6, 25, "孙八", 'F', "南京"};

// 定义一个函数来输出学生信息
void print_student_info(struct Student1 student);

int main()
{
  // 方式1:创建并初始化结构体变量
  struct Student1 stu1_1 = {1, 20, "张三", 'M', "北京"},
                  stu1_2 = {2, 21, "李四", 'F', "上海"};

  // 输出结构体变量的信息
  printf("方式1:\n");
  print_student_info(stu1_1);
  print_student_info(stu1_2);

  // printf("方式2:\n");
  // 强制类型转换
  // 在 C 语言中,不同类型之间的结构体不能直接进行强制类型转换,因为它们的内存布局可能不同。
  // print_student_info((struct Student1)stu2_1);   // 错误操作
  // print_student_info((struct Student1)stu2_1);   // 错误操作

  // printf("方式3:\n");
  // 强制类型转换
  // 在 C 语言中,不同类型之间的结构体不能直接进行强制类型转换,因为它们的内存布局可能不同。
  // print_student_info((struct Student1)stu3_1);   // 错误操作
  // print_student_info((struct Student1)stu3_1);   // 错误操作

  printf("方式2:\n");
  // 使用了复合字面量来创建一个新的 struct Student1类 型的临时变量
  print_student_info((struct Student1){stu2_1.id, stu2_1.age, stu2_1.name, stu2_1.gender, stu2_1.address});
  print_student_info((struct Student1){stu2_2.id, stu2_2.age, stu2_2.name, stu2_2.gender, stu2_2.address});

  printf("方式3:\n");
  // 使用了复合字面量来创建一个新的 struct Student1类 型的临时变量
  print_student_info((struct Student1){stu3_1.id, stu3_1.age, stu3_1.name, stu3_1.gender, stu3_1.address});
  print_student_info((struct Student1){stu3_2.id, stu3_2.age, stu3_2.name, stu3_2.gender, stu3_2.address});

  return 0;
}
// 定义一个函数来输出学生信息
void print_student_info(struct Student1 stu)
{
  char *genderStr = (stu.gender == 'M') ? "男" : (stu.gender == 'F') ? "女"
                                                                     : "未知";
  printf("学号: %d, 姓名: %s, 年龄: %d, 性别: %s, 地址: %s\n",
         stu.id, stu.name, stu.age, genderStr, stu.address);
}

        结构体类型在定义时指定了其成员和布局,因此不同类型的结构体即使成员相似,也被视为不同的类型。直接将一个结构体类型的变量转换为另一个不兼容的结构体类型是不安全的,也是未定义行为

        输出结果如下所示:

        但是,指针类型是可以进行转换的,如下所示:

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

// 方式1:先定义结构体,然后再创建结构体变量
struct Student1
{
    int id;        // 学号
    int age;       // 年龄
    char *name;    // 姓名
    char gender;   // 性别,建议使用字符型,例如 'M' 表示男性,'F' 表示女性
    char *address; // 家庭住址
};

// 方式2:在定义结构体的同时定义结构体变量
struct Student2
{
    int id;        // 学号
    int age;       // 年龄
    char *name;    // 姓名
    char gender;   // 性别,建议使用字符型,例如 'M' 表示男性,'F' 表示女性
    char *address; // 家庭住址
} stu2_1 = {3, 22, "王五", 'M', "广州"},
  stu2_2 = {4, 23, "赵六", 'F', "深圳"};

// 方式3:在定义时也可以不给出结构体名
struct
{
    int id;        // 学号
    int age;       // 年龄
    char *name;    // 姓名
    char gender;   // 性别,建议使用字符型,例如 'M' 表示男性,'F' 表示女性
    char *address; // 家庭住址
} stu3_1 = {5, 24, "钱七", 'M', "杭州"},
  stu3_2 = {6, 25, "孙八", 'F', "南京"};

// 定义一个函数来输出学生信息
// 接受一个指向 struct Student1 类型的指针
void print_student_info(struct Student1 *student);

int main()
{
    // 方式1:创建并初始化结构体变量
    struct Student1 stu1_1 = {1, 20, "张三", 'M', "北京"};
    struct Student1 stu1_2 = {2, 21, "李四", 'F', "上海"};

    // 输出结构体变量的信息
    printf("方式1:\n");
    print_student_info(&stu1_1);
    print_student_info(&stu1_2);

    printf("方式2:\n");
    // 通过使用 (struct Student1 *) 进行强制类型转换
    // 可以将 struct Student2 类型的指针或匿名结构体类型的指针转换为 struct Student1 类型的指针
    // 从而满足 print_student_info 函数的参数要求
    print_student_info((struct Student1 *)&stu2_1);
    print_student_info((struct Student1 *)&stu2_2);

    printf("方式3:\n");
    print_student_info((struct Student1 *)&stu3_1);
    print_student_info((struct Student1 *)&stu3_2);

    return 0;
}

// 定义一个函数来输出学生信息
void print_student_info(struct Student1 *student)
{
    char *gender_str = (student->gender == 'M') ? "男" : (student->gender == 'F') ? "女"
                                                                                  : "未知";
    printf("学号: %d, 姓名: %s, 年龄: %d, 性别: %s, 地址: %s\n",
           student->id, student->name, student->age, gender_str, student->address);
}

        在 C 语言中,将一种结构体类型的指针强制转换为另一种不兼容的结构体类型的指针确实是可行的,但这样做通常是不安全的,并且可能导致未定义行为

        输出结果如下所示:


8 编程练习

8.1 小狗案例

  • 编写一个 Dog 结构体,包含 name(char *)、age(int)、weight(double) 属性。
  • 编写一个 say 函数,返回字符串,方法返回信息中包含所有成员值。
  • 在 main 函数中,创建 Dog 结构体变量,调用 say 函数,将调用结果打印输出。
#include <stdio.h>
#include <string.h> // 用于 sprintf 函数

// 定义 Dog 结构体
struct Dog
{
    char *name;    // 名字
    int age;       // 年龄
    double weight; // 体重
};

// say 函数,返回字符串,信息中包含所有成员值
char *say(struct Dog dog);

int main()
{
    // 测试
    // 定义结构体变量
    struct Dog dog;
    // 空指针,安全行为
    char *dogInfo = NULL;

    // 初始化结构体变量
    dog.name = "小黄";
    dog.age = 1;
    dog.weight = 3.4;

    // 调用 say 函数,传入结构体变量(值传递)
    dogInfo = say(dog);

    // 打印返回的信息
    printf("小狗信息:%s \n", dogInfo);
}

// say 函数,返回字符串,信息中包含所有成员值
char *say(struct Dog dog)
{
    // 将这个信息放入到一个字符串(字符数组)
    // char *info; // 返回局部指针变量,错误!!!
    static char info[100]; // 局部静态变量,确保每次调用时内容不会被覆盖
    sprintf(info, "name=%s age=%d weight=%.2f", dog.name, dog.age, dog.weight);

    // 修改传入的结构体变量的 name 成员(仅在函数内部有效)
    dog.name = "小花";

    // 返回包含信息的字符串
    return info;
}

8.2 盒子案例

        编程创建一个 Box 结构体,在其中定义三个成员表示一个长方体的长、宽和高,长宽高可以通过控制台输入。

        定义一个函数获取长方体的体积(volume)。

        创建一个结构体指针,打印给定尺寸的长方体的体积。

#include <stdio.h>

// 定义 Box 结构体
struct Box
{
    double length; // 长度
    double width;  // 宽度
    double height; // 高度
};

// 获取立方体体积的函数
double getVolume(struct Box *box)
{
    // 通过指针访问结构体成员并计算体积
    // 指针形式可以在里面修改原始数据
    // 值传递无法改变结构体的原始数据
    return box->length * box->width * box->height;
}

int main()
{
    // 创建结构体变量
    struct Box box;

    // 创建结构体指针,并将其初始化为结构体变量的地址
    struct Box *cube = &box;

    // 提示用户输入长度
    printf("Enter length: ");
    // 使用指针访问结构体成员,并读取用户输入
    scanf("%lf", &cube->length);

    // 提示用户输入宽度
    printf("Enter width: ");
    // 使用指针访问结构体成员,并读取用户输入
    scanf("%lf", &cube->width);

    // 提示用户输入高度
    printf("Enter height: ");
    // 使用指针访问结构体成员,并读取用户输入
    scanf("%lf", &cube->height);

    // 调用函数获取体积并打印
    printf("Volume is: %.2f\n", getVolume(cube));

    return 0;
}

        输出结果如下所示:

8.3 景区门票案例

        一个景区根据游人的年龄收取不同价格的门票。

        请编写游人结构体(Visitor),根据年龄段决定能够购买的门票价格并输出。

        规则:年龄>18,门票为20元,其它情况免费。

        可以循环从控制台输入名字和年龄,打印门票收费情况,如果名字输入n,则退出程序。

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

// 定义 Visitor 结构体
struct Visitor
{
    char name[10]; // 游客名字
    int age;       // 游客年龄
    double pay;    // 应付票价
};

// 编写函数处理业务
void ticket(struct Visitor *visitor)
{
    // 根据年龄判断票价
    if (visitor->age > 18)
    {
        visitor->pay = 20.0; // 成年人票价 20 元
    }
    else
    {
        visitor->pay = 0.0; // 未成年人免费
    }
}

int main()
{
    // 测试
    // 创建结构体变量(创建一个游客)
    struct Visitor visitor;

    // 循环的输入名字和年龄
    while (1)
    {
        printf("\n 请输入游客名字:");
        scanf("%s", visitor.name);

        // 判断如果名字输入 "n" ,则退出程序
        if (!strcmp("n", visitor.name))
        {
            break;
        }

        printf("\n 请输入游客年龄:");
        scanf("%d", &visitor.age);

        // 调用函数 ticket , 获取应付的票价
        ticket(&visitor);

        // 打印应付的票价
        printf("\n 该游客应付票价:%.2f\n", visitor.pay);
    }

    printf("退出程序\n");

    return 0;
}

        输出结果如下所示:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2197867.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于STM32的智能鱼缸自动喂食系统设计

引言 本项目设计了一个基于STM32的智能鱼缸自动喂食系统&#xff0c;能够按照预设的时间间隔自动投放饲料&#xff0c;同时监测鱼缸内的水温和光照情况。该系统通过电机控制喂食器的旋转来实现饲料投放&#xff0c;用户还可以通过按键实现手动喂食。该项目展示了STM32在定时控…

Spring Validation —— 参数校验框架

案例说明——后端校验注册表单字段 在编写注册功能时&#xff0c;需要考虑字段校验的情况&#xff0c;这时候可以采用 Spring提供的一套参数校验框架工具——Spring Validation。一下是使用的步骤&#xff1a; 1. 导入validation坐标 2. 在参数上添加 Pattern注解&#xff0c…

单细胞|Signac 进行 Motif 分析

单细胞|Signac 进行 Motif 分析 引言 本教程将指导您如何在Signac平台上进行DNA序列的基序(Motif)分析。会介绍两种基序分析的方法&#xff1a;一种是在一组差异可访问的峰值中寻找出现频率较高的基序&#xff1b;另一种是在不同细胞群组间进行基序活性的差异分析。 library(Si…

simpread-OpenAI推出Canvas:让ChatGPT成为更出色的项目协作者

引言 OpenAI在最新一轮融资中创下了VC融资历史上的记录&#xff0c;与此同时&#xff0c;他们也推出了一项令人瞩目的新功能——Canvas。Canvas是一个专门为项目协作设计的界面&#xff0c;旨在让ChatGPT成为更高效的项目协作者。在现有的ChatGPT应用之外&#xff0c;它打开了…

LC538 - 把二叉搜索树转换为累加树

文章目录 1 题目2 思路3 ACM模式参考 1 题目 https://leetcode.cn/problems/convert-bst-to-greater-tree/description/ 给出二叉 搜索 树的根节点&#xff0c;该树的节点值各不相同&#xff0c;请你将其转换为累加树&#xff08;Greater Sum Tree&#xff09; 累加树&#…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-08

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-07 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-07目录1. GraphRouter: A Graph-based Router for LLM Selections摘要创新点算法模型实验效果结论推荐阅读指数 2. DOTS: Learni…

2024年诺贝尔物理学奖授予AI先驱 Hopfield 和Hinton,奖金1100万瑞典克朗,他们是ChatGPT的忠实用户

2024年诺贝尔物理学奖得主&#xff1a;AI领域的杰出科学家 获奖者 2024年诺贝尔物理学奖的获奖者是两位在人工智能&#xff08;AI&#xff09;尤其是机器学习技术方面做出杰出贡献的科学家&#xff1a;约翰霍普菲尔德和杰弗里欣顿。 瑞典皇家科学院在周二宣布&#xff0c;这两…

Shamiko模块:隐藏root

开启Zygisk 在安装Shamiko模块之前&#xff0c;需要使用面具开启Zygisk&#xff0c;在面具的设置里可以开启&#xff0c;开启后重启手机Zygisk生效。 开启后的样子 开启面具随机报名 在面具的设置里&#xff0c;找到“隐藏Magisk应用”&#xff0c;点击这个选项&#xff0…

ESP8266使用AT指令完成MQTT功能

ESP8266使用AT指令完成MQTT功能 在esp8266设备中烧录安信可的AT固件之后&#xff0c;进行AT指令完成信息发布&#xff0c;并最终实现在Homeassistant中发布传感器并设置传感器状态。 一、基础指令 以下是完整的步骤和对应的AT指令&#xff1a; 1. 配置ESP8266为Station模式 …

在线培训知识库管理系统:企业的明智之选

在当今这个快速变化的时代&#xff0c;企业之间的竞争日益激烈&#xff0c;知识更新速度之快前所未有。为了保持竞争力&#xff0c;企业不仅需要不断引入新技术、新方法&#xff0c;还需要确保员工能够及时掌握这些新知&#xff0c;将其转化为生产力。在此背景下&#xff0c;在…

codetop标签双指针题目大全解析(四),双指针刷穿地心!!!!!

复习复习复习&#xff01;&#xff01; 1.长度最小的子数组2.移动零3.盛水最多的容器4.旋转链表5.最接近的三数之和6.删除有序数组中的重复项7.返回倒数第k个节点的值8.四数之和9.验证回文串 1.长度最小的子数组 考滑动窗口的 要注意是大于等于不是等于 看错题目一顿调 class …

CNAI趋势下,打造一体化AI赋能平台

在数字化转型的浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;已成为推动企业创新和转型的核心力量。云原生技术以其灵活性和可扩展性&#xff0c;为AI的应用和发展提供了坚实的基础。本文将探讨云原生人工智能&#xff08;CNAI&#xff09;如何为企业带来颠覆性的变革…

cnn突破八(两层卷积核bpnet网络扩展)

cnn突破七中x【&#xff1f;】怎么求&#xff1f;我们举个例子&#xff1a; 接着cnn突破七&#xff1a; hicnn【】来自temphicnn【】2*2最大池化&#xff1a; temphicnn[0]x[i0,j0,5*5方阵]*w1cnn[0-24]&#xff0c; hicnn是5*5的&#xff0c;temphicnn是10*10的&#xff0…

YOLOv8改进 - 注意力篇 - 引入SimAM注意力机制

一、本文介绍 作为入门性篇章&#xff0c;这里介绍了SimAM注意力在YOLOv8中的使用。包含SimAM原理分析&#xff0c;SimAM的代码、SimAM的使用方法、以及添加以后的yaml文件及运行记录。 二、SimAM原理分析 SimAM官方论文地址&#xff1a;SimAM文章 SimAM官方代码地址&#…

【土地那些事儿】一文读懂“集体所有土地”

今天咱们来聊一个既熟悉又神秘的概念——集体所有土地。在中国广袤的大地上&#xff0c;除了国有土地外&#xff0c;还有一种特殊的土地所有权形式&#xff0c;它关乎亿万农民的切身利益&#xff0c;那就是集体所有土地。来&#xff0c;让我们一起揭开它的面纱吧&#xff01; …

根据指数做波段年化利率分析

根据指数做波段年化利率分析 股票投资&#xff0c;是众多投资方式中的一种。然而&#xff0c;每个人有不同的炒股方式&#xff0c;对股票不同的操作也会获得不同的收益/损失。作为“金融消费者”&#xff0c;如何做好自己在股票中的消费行为&#xff0c;是一门巨大的学问。这里…

D31【python 接口自动化学习】- python基础之输入输出与文件操作

day31 文件的打开 学习日期&#xff1a;20241008 学习目标&#xff1a;输入输出与文件操作&#xfe63;-43 常见常新&#xff1a;文件的打开 学习笔记&#xff1a; 文件的概念 使用open()函数打开文件 文件路径处理 文件打开模式 总结 文件操作包括&#xff1a;打开&#…

C++:string (用法篇)

文章目录 前言一、string 是什么&#xff1f;二、C语法补充1. auto2. 范围for 三、string类对象的常见构造1. Construct string object2. String destructor3. operator 四、string迭代器相关1. begin与end1&#xff09;begin2&#xff09;end3&#xff09;使用 2. rbegin 与 r…

第33次CCF计算机软件能力认证-第4题十滴水

题干&#xff1a; 十滴水是一个非常经典的小游戏。 小 C C C 正在玩一个一维版本的十滴水游戏。 我们通过一个例子描述游戏的基本规则。 游戏在一个 1 c 1c 1c 的网格上进行&#xff0c;格子用整数 x ( 1 ≤ x ≤ c ) x(1≤x≤c) x(1≤x≤c) 编号&#xff0c;编号从左往…

Metal之旅——数据

顶点数据 顶点数据&#xff08;Vertex Data&#xff09;是指一系列顶点的集合&#xff0c;这些顶点用于图形渲染管线的输入。每个顶点通常包含位置、颜色、纹理坐标等属性信息 mesh网络 Mesh网络是指metal中通过将不同数量形状的三角形拼在一起覆盖在要绘制图形表面的网络 顶…