«

C语言结构体完全教程

从0至1 • 1 个月前 • 91 次点击 • C


C语言结构体完全教程

一、结构体基本概念

结构体(struct)是C语言中一种重要的自定义数据类型,它允许我们将不同类型的数据组合在一起,形成一个新的数据类型。结构体可以看作是一组相关变量的集合,这些变量可以是不同类型的,它们共同描述一个对象的属性。

为什么需要结构体?


二、结构体的定义与声明

2.1 结构体的定义

结构体定义的语法格式:

struct 结构体名 {
    数据类型 成员名1;
    数据类型 成员名2;
    // ... 更多成员
};

示例:定义一个学生结构体

struct Student {
    int id;             // 学号
    char name[20];      // 姓名
    int age;            // 年龄
    float score;        // 分数
    char gender;        // 性别
};

2.2 结构体变量的声明

有三种方式声明结构体变量:

方式1:先定义结构体类型,再声明变量

struct Student {
    int id;
    char name[20];
    int age;
};

struct Student stu1, stu2;  // 声明两个Student类型的变量

方式2:定义结构体类型的同时声明变量

struct Student {
    int id;
    char name[20];
    int age;
} stu1, stu2;  // 直接在定义后声明变量

方式3:匿名结构体(不指定结构体名)

struct {
    int id;
    char name[20];
    int age;
} stu1, stu2;  // 只能在这里声明变量,后续无法再创建该类型变量

三、结构体的初始化

3.1 按顺序初始化

struct Student stu = {1001, "张三", 18, 95.5, 'M'};

3.2 指定成员初始化(C99及以上支持)

struct Student stu = {
    .id = 1001,
    .name = "张三",
    .age = 18,
    .score = 95.5,
    .gender = 'M'
};

3.3 部分初始化

struct Student stu = {1001, "李四"};  // 未初始化的成员自动为0或NULL

3.4 定义时直接初始化

struct Student {
    int id;
    char name[20];
    int age;
} stu = {1001, "王五", 20};

四、结构体成员的访问方式

4.1 使用点运算符(.)访问普通结构体变量

struct Student stu;

// 赋值
stu.id = 1001;
strcpy(stu.name, "张三");
stu.age = 18;

// 读取
printf("学号:%d\n", stu.id);
printf("姓名:%s\n", stu.name);
printf("年龄:%d\n", stu.age);

4.2 使用箭头运算符(->)访问结构体指针

struct Student stu;
struct Student *p = &stu;

// 通过指针访问成员(两种方式等价)
p->id = 1001;           // 方式1:箭头运算符(推荐)
(*p).age = 18;          // 方式2:先解引用再用点运算符

printf("学号:%d\n", p->id);

*注意:箭头运算符是C语言的语法糖,p->id 等价于 `(p).id`**


五、结构体数组

结构体数组是指数组中的每个元素都是结构体类型。

5.1 结构体数组的定义和初始化

// 定义一个包含3个学生的数组
struct Student class[3] = {
    {1001, "张三", 18, 95.5, 'M'},
    {1002, "李四", 19, 88.0, 'F'},
    {1003, "王五", 18, 92.5, 'M'}
};

5.2 结构体数组的访问

// 访问第2个学生的姓名
printf("第二个学生:%s\n", class[1].name);

// 遍历所有学生
for (int i = 0; i < 3; i++) {
    printf("学号:%d, 姓名:%s, 分数:%.1f\n", 
           class[i].id, class[i].name, class[i].score);
}

六、结构体指针

6.1 指向结构体变量的指针

struct Student stu = {1001, "张三", 18, 95.5, 'M'};
struct Student *p = &stu;

// 通过指针访问成员
printf("姓名:%s\n", p->name);
printf("分数:%.1f\n", p->score);

6.2 结构体指针数组

struct Student stu1 = {1001, "张三", 18, 95.5, 'M'};
struct Student stu2 = {1002, "李四", 19, 88.0, 'F'};

struct Student *students[2] = {&stu1, &stu2};

// 访问
printf("%s\n", students[0]->name);  // 张三
printf("%s\n", students[1]->name);  // 李四

6.3 动态分配结构体(malloc)

// 动态分配一个结构体
struct Student *p = (struct Student *)malloc(sizeof(struct Student));

if (p != NULL) {
    p->id = 1001;
    strcpy(p->name, "张三");
    p->age = 18;

    printf("动态创建的学生:%s\n", p->name);

    free(p);  // 释放内存
}

七、结构体作为函数参数

7.1 值传递(传递结构体副本)

// 函数定义
void printStudent(struct Student s) {
    printf("学号:%d, 姓名:%s\n", s.id, s.name);
}

// 函数调用
struct Student stu = {1001, "张三", 18, 95.5, 'M'};
printStudent(stu);  // 传递整个结构体(会创建副本)

缺点:结构体较大时,值传递效率低

7.2 地址传递(传递结构体指针,推荐)

// 函数定义
void printStudent(const struct Student *s) {
    printf("学号:%d, 姓名:%s\n", s->id, s->name);
}

// 修改结构体成员的函数
void setScore(struct Student *s, float newScore) {
    s->score = newScore;
}

// 函数调用
struct Student stu = {1001, "张三", 18, 95.5, 'M'};
printStudent(&stu);
setScore(&stu, 100.0);

优点:只传递地址,效率高,还可以修改原结构体

7.3 返回结构体

// 函数返回结构体
struct Student createStudent(int id, const char *name, int age) {
    struct Student s;
    s.id = id;
    strcpy(s.name, name);
    s.age = age;
    s.score = 0;
    s.gender = 'U';
    return s;
}

// 使用
struct Student newStu = createStudent(1005, "赵六", 20);

八、嵌套结构体

结构体的成员也可以是另一个结构体类型。

8.1 嵌套结构体定义

// 日期结构体
struct Date {
    int year;
    int month;
    int day;
};

// 学生结构体(包含日期成员)
struct Student {
    int id;
    char name[20];
    struct Date birthday;  // 嵌套结构体
    struct Date enrollDate;
};

8.2 嵌套结构体初始化

struct Student stu = {
    1001,
    "张三",
    {2005, 5, 15},      // birthday
    {2023, 9, 1}        // enrollDate
};

8.3 嵌套结构体访问

// 连续使用点运算符
printf("出生年份:%d\n", stu.birthday.year);
printf("入学月份:%d\n", stu.enrollDate.month);

// 如果是指针
struct Student *p = &stu;
printf("出生日:%d\n", p->birthday.day);

九、typedef用法

typedef 用于为已有的数据类型创建别名,简化结构体的使用。

9.1 基本用法

// 方式1:先定义结构体,再typedef
struct Student {
    int id;
    char name[20];
    int age;
};
typedef struct Student Student;  // 创建别名Student

// 现在可以直接用Student声明变量
Student stu1, stu2;  // 不需要写struct Student

9.2 定义结构体时同时typedef(推荐写法)

typedef struct {
    int id;
    char name[20];
    int age;
    float score;
} Student;  // 结构体别名为Student

// 使用
Student stu = {1001, "张三", 18, 95.5};

9.3 typedef结构体指针

typedef struct {
    int id;
    char name[20];
} Student;

typedef Student* StudentPtr;  // 结构体指针别名

// 使用
StudentPtr p = (StudentPtr)malloc(sizeof(Student));
p->id = 1001;

9.4 typedef的好处

  1. 简化代码:不需要每次都写 struct 关键字
  2. 提高可读性:类型名更清晰
  3. 便于移植:修改类型定义时只需修改一处

十、代码练习题

练习题1:学生信息管理

题目:
定义一个学生结构体,包含学号、姓名、年龄、分数。编写程序实现:

  1. 输入3个学生的信息
  2. 计算并输出平均分
  3. 找出分数最高的学生并输出

参考代码:

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

typedef struct {
    int id;
    char name[20];
    int age;
    float score;
} Student;

int main() {
    Student students[3];
    float total = 0;
    int maxIndex = 0;

    // 输入学生信息
    for (int i = 0; i < 3; i++) {
        printf("请输入第%d个学生信息:\n", i + 1);
        printf("学号:");
        scanf("%d", &students[i].id);
        printf("姓名:");
        scanf("%s", students[i].name);
        printf("年龄:");
        scanf("%d", &students[i].age);
        printf("分数:");
        scanf("%f", &students[i].score);

        total += students[i].score;

        // 记录最高分
        if (students[i].score > students[maxIndex].score) {
            maxIndex = i;
        }
    }

    // 输出平均分
    printf("\n平均分:%.2f\n", total / 3);

    // 输出最高分学生
    printf("\n分数最高的学生:\n");
    printf("学号:%d, 姓名:%s, 分数:%.1f\n",
           students[maxIndex].id,
           students[maxIndex].name,
           students[maxIndex].score);

    return 0;
}

练习题2:图书管理系统

题目:
定义图书结构体(书名、作者、价格、出版日期),实现以下功能:

  1. 使用嵌套结构体表示日期
  2. 实现函数打印图书信息
  3. 实现函数比较两本书的价格

参考代码:

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

// 日期结构体
typedef struct {
    int year;
    int month;
    int day;
} Date;

// 图书结构体
typedef struct {
    char title[50];
    char author[30];
    float price;
    Date publishDate;
} Book;

// 打印图书信息
void printBook(const Book *book) {
    printf("书名:%s\n", book->title);
    printf("作者:%s\n", book->author);
    printf("价格:%.2f元\n", book->price);
    printf("出版日期:%d年%d月%d日\n",
           book->publishDate.year,
           book->publishDate.month,
           book->publishDate.day);
}

// 比较价格,返回较贵的书
Book* comparePrice(Book *b1, Book *b2) {
    return (b1->price > b2->price) ? b1 : b2;
}

int main() {
    Book book1 = {
        "C语言程序设计",
        "谭浩强",
        39.80,
        {2021, 6, 1}
    };

    Book book2 = {
        "数据结构与算法",
        "严蔚敏",
        45.00,
        {2020, 8, 15}
    };

    printf("=== 图书1 ===\n");
    printBook(&book1);

    printf("\n=== 图书2 ===\n");
    printBook(&book2);

    printf("\n价格较高的书是:%s\n", comparePrice(&book1, &book2)->title);

    return 0;
}

练习题3:员工工资计算

题目:
定义员工结构体(工号、姓名、基本工资、奖金、扣款),编写程序:

  1. 计算实发工资(基本工资+奖金-扣款)
  2. 使用结构体数组存储多个员工
  3. 按实发工资降序排序并输出

参考代码:

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

typedef struct {
    int id;
    char name[20];
    float baseSalary;   // 基本工资
    float bonus;        // 奖金
    float deduction;    // 扣款
    float netSalary;    // 实发工资
} Employee;

// 计算实发工资
void calculateNetSalary(Employee *emp) {
    emp->netSalary = emp->baseSalary + emp->bonus - emp->deduction;
}

// 按实发工资排序(冒泡排序)
void sortBySalary(Employee emps[], int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (emps[j].netSalary < emps[j + 1].netSalary) {
                Employee temp = emps[j];
                emps[j] = emps[j + 1];
                emps[j + 1] = temp;
            }
        }
    }
}

// 打印员工工资表
void printSalaryTable(Employee emps[], int n) {
    printf("%-6s%-10s%-12s%-10s%-10s%-12s\n",
           "工号", "姓名", "基本工资", "奖金", "扣款", "实发工资");
    printf("--------------------------------------------------------\n");

    for (int i = 0; i < n; i++) {
        printf("%-6d%-10s%-12.2f%-10.2f%-10.2f%-12.2f\n",
               emps[i].id,
               emps[i].name,
               emps[i].baseSalary,
               emps[i].bonus,
               emps[i].deduction,
               emps[i].netSalary);
    }
}

int main() {
    Employee emps[] = {
        {1001, "张三", 8000, 2000, 500},
        {1002, "李四", 6500, 1500, 300},
        {1003, "王五", 9000, 3000, 800},
        {1004, "赵六", 7500, 1800, 400}
    };

    int n = sizeof(emps) / sizeof(emps[0]);

    // 计算每个人的实发工资
    for (int i = 0; i < n; i++) {
        calculateNetSalary(&emps[i]);
    }

    printf("=== 排序前 ===\n");
    printSalaryTable(emps, n);

    // 排序
    sortBySalary(emps, n);

    printf("\n=== 按实发工资降序排序后 ===\n");
    printSalaryTable(emps, n);

    return 0;
}

总结

结构体是C语言中非常重要的数据类型,掌握它需要理解:

  1. 结构体的本质:将不同类型数据组织在一起
  2. 两种访问方式. 用于普通变量,-> 用于指针
  3. 函数传参:优先使用指针传递,效率更高
  4. 嵌套结构体:成员可以是另一个结构体
  5. typedef:简化类型名,提高代码可读性

通过多做练习,你就能熟练掌握结构体的使用,为后续学习数据结构和面向对象编程打下坚实基础!


扫描二维码,在手机上阅读
文章目录


    收藏
    还没收到回复
    请先 登录 再回复