Skip to content

结构体

结构体是C语言中用于组合不同类型数据的数据结构。通过结构体,可以将相关的数据组织在一起,形成自定义的数据类型。本章将详细介绍结构体、联合体和枚举的使用。

结构体基础

结构体的定义

c
/*
 * 结构体的定义和使用
 */

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

/* 定义结构体类型 */
struct Student {
    char name[50];      /* 姓名 */
    int age;            /* 年龄 */
    float score;        /* 成绩 */
};

/* 使用typedef定义结构体别名 */
typedef struct {
    int x;
    int y;
} Point;

/* 嵌套结构体 */
struct Date {
    int year;
    int month;
    int day;
};

struct Person {
    char name[50];
    struct Date birthday;  /* 嵌套的结构体 */
};

int main(void)
{
    /* 声明结构体变量 */
    struct Student stu1;
    
    /* 初始化方式1:逐个赋值 */
    strcpy(stu1.name, "张三");
    stu1.age = 20;
    stu1.score = 85.5;
    
    /* 初始化方式2:声明时初始化 */
    struct Student stu2 = {"李四", 21, 90.0};
    
    /* 初始化方式3:指定成员初始化(C99) */
    struct Student stu3 = {
        .name = "王五",
        .age = 22,
        .score = 88.0
    };
    
    /* 访问结构体成员 */
    printf("学生1: %s, %d岁, 成绩%.1f\n", 
           stu1.name, stu1.age, stu1.score);
    printf("学生2: %s, %d岁, 成绩%.1f\n", 
           stu2.name, stu2.age, stu2.score);
    
    /* 使用typedef定义的结构体 */
    Point p1 = {10, 20};
    Point p2 = {.x = 5, .y = 15};
    
    printf("\n点1: (%d, %d)\n", p1.x, p1.y);
    printf("点2: (%d, %d)\n", p2.x, p2.y);
    
    /* 嵌套结构体 */
    struct Person person = {
        "张三",
        {2000, 5, 15}
    };
    
    printf("\n姓名: %s\n", person.name);
    printf("生日: %d%d%d\n", 
           person.birthday.year, 
           person.birthday.month, 
           person.birthday.day);
    
    return 0;
}

结构体数组

c
/*
 * 结构体数组
 */

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

/* 定义结构体 */
struct Student {
    char name[50];
    int age;
    float score;
};

/* 打印学生信息 */
void print_student(struct Student s)
{
    printf("%-10s %3d %6.1f\n", s.name, s.age, s.score);
}

int main(void)
{
    /* 声明结构体数组 */
    struct Student class1[3];
    
    /* 初始化数组元素 */
    strcpy(class1[0].name, "张三");
    class1[0].age = 20;
    class1[0].score = 85.5;
    
    strcpy(class1[1].name, "李四");
    class1[1].age = 21;
    class1[1].score = 90.0;
    
    strcpy(class1[2].name, "王五");
    class1[2].age = 19;
    class1[2].score = 78.5;
    
    /* 声明时初始化 */
    struct Student class2[] = {
        {"赵六", 22, 88.0},
        {"钱七", 20, 92.5},
        {"孙八", 21, 76.0}
    };
    
    /* 遍历结构体数组 */
    printf("班级1学生信息:\n");
    printf("%-10s %3s %6s\n", "姓名", "年龄", "成绩");
    for (int i = 0; i < 3; i++) {
        print_student(class1[i]);
    }
    
    /* 计算平均成绩 */
    float total = 0;
    int count = sizeof(class2) / sizeof(class2[0]);
    
    for (int i = 0; i < count; i++) {
        total += class2[i].score;
    }
    
    printf("\n班级2平均成绩: %.2f\n", total / count);
    
    return 0;
}

结构体指针

c
/*
 * 结构体指针
 */

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

struct Student {
    char name[50];
    int age;
    float score;
};

/* 通过指针访问结构体成员 */
void print_student_ptr(struct Student *s)
{
    /* 使用 -> 运算符 */
    printf("姓名: %s\n", s->name);
    printf("年龄: %d\n", s->age);
    printf("成绩: %.1f\n", s->score);
    
    /* 等价于 (*s).name */
    printf("(*s).name = %s\n", (*s).name);
}

/* 通过指针修改结构体 */
void update_score(struct Student *s, float new_score)
{
    s->score = new_score;
}

int main(void)
{
    struct Student stu = {"张三", 20, 85.5};
    struct Student *ptr = &stu;
    
    /* 使用指针访问成员 */
    printf("使用指针访问:\n");
    printf("ptr->name = %s\n", ptr->name);
    printf("ptr->age = %d\n", ptr->age);
    printf("ptr->score = %.1f\n", ptr->score);
    
    /* 通过指针修改 */
    ptr->age = 21;
    printf("\n修改后年龄: %d\n", stu.age);
    
    /* 动态分配结构体 */
    struct Student *dynamic_stu = (struct Student*)malloc(sizeof(struct Student));
    if (dynamic_stu != NULL) {
        strcpy(dynamic_stu->name, "李四");
        dynamic_stu->age = 22;
        dynamic_stu->score = 90.0;
        
        printf("\n动态分配的结构体:\n");
        print_student_ptr(dynamic_stu);
        
        free(dynamic_stu);
    }
    
    /* 结构体指针数组 */
    struct Student *students[3];
    struct Student s1 = {"张三", 20, 85};
    struct Student s2 = {"李四", 21, 90};
    struct Student s3 = {"王五", 22, 88};
    
    students[0] = &s1;
    students[1] = &s2;
    students[2] = &s3;
    
    printf("\n结构体指针数组:\n");
    for (int i = 0; i < 3; i++) {
        printf("%s: %.1f\n", students[i]->name, students[i]->score);
    }
    
    return 0;
}

结构体与函数

c
/*
 * 结构体作为函数参数和返回值
 */

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

struct Point {
    int x;
    int y;
};

/* 结构体作为参数(值传递) */
void print_point(struct Point p)
{
    printf("(%d, %d)\n", p.x, p.y);
}

/* 结构体指针作为参数(地址传递) */
void move_point(struct Point *p, int dx, int dy)
{
    p->x += dx;
    p->y += dy;
}

/* 返回结构体 */
struct Point create_point(int x, int y)
{
    struct Point p = {x, y};
    return p;
}

/* 返回结构体指针(动态分配) */
struct Point *create_point_dynamic(int x, int y)
{
    struct Point *p = (struct Point*)malloc(sizeof(struct Point));
    if (p != NULL) {
        p->x = x;
        p->y = y;
    }
    return p;
}

/* 结构体数组作为参数 */
void print_points(struct Point points[], int n)
{
    for (int i = 0; i < n; i++) {
        printf("Point %d: (%d, %d)\n", i, points[i].x, points[i].y);
    }
}

int main(void)
{
    struct Point p1 = {10, 20};
    
    /* 值传递 */
    printf("值传递:\n");
    print_point(p1);
    
    /* 指针传递 */
    printf("\n移动前: ");
    print_point(p1);
    move_point(&p1, 5, -5);
    printf("移动后: ");
    print_point(p1);
    
    /* 返回结构体 */
    printf("\n创建新点:\n");
    struct Point p2 = create_point(30, 40);
    print_point(p2);
    
    /* 返回结构体指针 */
    struct Point *p3 = create_point_dynamic(50, 60);
    if (p3 != NULL) {
        printf("\n动态创建的点: ");
        print_point(*p3);
        free(p3);
    }
    
    return 0;
}

联合体

c
/*
 * 联合体(共用体)
 */

#include <stdio.h>

/* 联合体:所有成员共享同一块内存 */
union Data {
    int i;
    float f;
    char c;
    char str[4];
};

/* 联合体的大小是最大成员的大小 */
int main(void)
{
    union Data data;
    
    printf("联合体大小: %zu 字节\n", sizeof(union Data));
    printf("int大小: %zu\n", sizeof(int));
    printf("float大小: %zu\n", sizeof(float));
    printf("char大小: %zu\n", sizeof(char));
    
    /* 存储整数 */
    data.i = 10;
    printf("\n存储整数: %d\n", data.i);
    
    /* 存储浮点数(覆盖之前的值) */
    data.f = 3.14f;
    printf("存储浮点数: %f\n", data.f);
    printf("此时整数: %d(无意义)\n", data.i);
    
    /* 存储字符 */
    data.c = 'A';
    printf("\n存储字符: %c\n", data.c);
    
    /* 实际应用:类型标记 */
    struct Value {
        int type;  /* 0=int, 1=float, 2=char */
        union {
            int i_val;
            float f_val;
            char c_val;
        } data;
    };
    
    struct Value v1;
    v1.type = 0;
    v1.data.i_val = 100;
    
    struct Value v2;
    v2.type = 1;
    v2.data.f_val = 3.14f;
    
    printf("\n类型标记联合体:\n");
    printf("v1: type=%d, value=", v1.type);
    if (v1.type == 0) printf("%d\n", v1.data.i_val);
    
    printf("v2: type=%d, value=", v2.type);
    if (v2.type == 1) printf("%f\n", v2.data.f_val);
    
    return 0;
}

枚举

c
/*
 * 枚举类型
 */

#include <stdio.h>

/* 定义枚举 */
enum WeekDay {
    SUNDAY,
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY
};

/* 指定值的枚举 */
enum Month {
    JANUARY = 1,
    FEBRUARY = 2,
    MARCH = 3,
    APRIL = 4,
    MAY = 5,
    JUNE = 6,
    JULY = 7,
    AUGUST = 8,
    SEPTEMBER = 9,
    OCTOBER = 10,
    NOVEMBER = 11,
    DECEMBER = 12
};

/* 枚举在结构体中的应用 */
struct Task {
    char name[50];
    enum {
        TODO,
        IN_PROGRESS,
        DONE
    } status;
    int priority;
};

/* 使用typedef定义枚举别名 */
typedef enum {
    RED,
    GREEN,
    BLUE
} Color;

/* 枚举作为函数参数 */
const char* get_weekday_name(enum WeekDay day)
{
    switch (day) {
        case SUNDAY: return "星期日";
        case MONDAY: return "星期一";
        case TUESDAY: return "星期二";
        case WEDNESDAY: return "星期三";
        case THURSDAY: return "星期四";
        case FRIDAY: return "星期五";
        case SATURDAY: return "星期六";
        default: return "未知";
    }
}

int main(void)
{
    /* 使用枚举 */
    enum WeekDay today = WEDNESDAY;
    printf("今天是: %s (值=%d)\n", get_weekday_name(today), today);
    
    /* 遍历枚举 */
    printf("\n一周七天:\n");
    for (enum WeekDay day = SUNDAY; day <= SATURDAY; day++) {
        printf("%d: %s\n", day, get_weekday_name(day));
    }
    
    /* 枚举在结构体中 */
    struct Task task1 = {"写代码", IN_PROGRESS, 1};
    struct Task task2 = {"测试", TODO, 2};
    
    printf("\n任务列表:\n");
    printf("%s: 状态=%d, 优先级=%d\n", 
           task1.name, task1.status, task1.priority);
    
    /* 使用typedef的枚举 */
    Color favorite = BLUE;
    printf("\n喜欢的颜色: %d\n", favorite);
    
    return 0;
}

位域

c
/*
 * 位域(位字段)
 */

#include <stdio.h>

/* 位域结构体 */
struct Flags {
    unsigned int ready : 1;     /* 1位 */
    unsigned int error : 1;     /* 1位 */
    unsigned int mode : 2;      /* 2位,可存储0-3 */
    unsigned int count : 4;     /* 4位,可存储0-15 */
};

/* 实际应用:IP地址头 */
struct IPHeader {
    unsigned int version : 4;       /* 版本号 */
    unsigned int header_len : 4;    /* 头长度 */
    unsigned int tos : 8;           /* 服务类型 */
    unsigned int total_len : 16;    /* 总长度 */
};

int main(void)
{
    struct Flags f;
    
    /* 设置位域值 */
    f.ready = 1;
    f.error = 0;
    f.mode = 3;    /* 最大值是3(2位) */
    f.count = 15;  /* 最大值是15(4位) */
    
    printf("位域结构体大小: %zu 字节\n", sizeof(struct Flags));
    
    printf("\nFlags值:\n");
    printf("ready: %u\n", f.ready);
    printf("error: %u\n", f.error);
    printf("mode: %u\n", f.mode);
    printf("count: %u\n", f.count);
    
    /* 超出范围的值会被截断 */
    f.count = 20;  /* 20 = 10100,只存储低4位0100 = 4 */
    printf("\ncount = 20后: %u(被截断)\n", f.count);
    
    return 0;
}

小结

本章学习了:

  • 结构体:定义、初始化、成员访问、结构体数组、结构体指针
  • 结构体与函数:值传递、指针传递、返回结构体
  • 联合体:共享内存,节省空间
  • 枚举:命名常量,提高代码可读性
  • 位域:节省内存空间

下一章,我们将学习文件操作,了解C语言的文件读写操作。