解码C语言复合数据类型之结构体

结构体的定义与声明

结构体是一种 自定义数据类型,用于将多个不同类型的变量组合成一个整体。

定义结构体类型

struct Student {
    char name[20];// 字符数组成员
    int age;// 整型成员
    float score;// 浮点型成员
};

统一 typedef 定义

确保通过 typedef 定义的类型名全局唯一:

//在头文件中统一定义
typedef struct Point {
    int x;
    int y;
} Point;// struct Point 和 Point 类型一致

声明结构体变量

// 方式1:先定义类型,再声明变量
struct Student stu1;

// 方式2:定义类型时直接声明变量(了解)
struct Book {
    char title[50];
    float price;
} book1, book2;// 同时声明book1和book2

省略标签声明方式(声明后不可以定义变量)

// 省略标签,直接定义变量
struct {
    char name[50];
    int age;
    float height;
} person1, person2;// 直接声明变量
// 或者使用typedef创建别名
typedef struct {
    char name[50];
    int age;
    float height;
} Person;// Person现在是类型名

Person person3;// 可以直接使用

使用省略标签的场景(嵌套结构体)

#include <stdio.h>
typedef struct {
// 内嵌匿名结构体,只在DateTime中使用,其他地方不能定义与date同型结构体
	struct {
	        int hour;
	        int minute;
	        int second;
	} time;

    struct {
        int year;
        int month;
        int day;
    } date;
} DateTime;

int main() {
    DateTime dt = {{12, 30, 45}, {2024, 1, 15}};
    printf("时间: %02d:%02d:%02d\n",
           dt.time.hour, dt.time.minute, dt.time.second);
    return 0;
}

结构体的初始化

直接初始化

在声明时直接赋值:

struct Student stu1 = {"Alice", 18, 95.5};

按成员初始化(C99标准)

指定成员赋值,顺序可调:

struct Student stu2 = {
    .age = 20,
    .name = "Bob",
    .score = 88.0
};

动态初始化

通过代码逐个赋值:

struct Student stu3;
strcpy(stu3.name, "Charlie");// 字符串需用strcpy
stu3.age = 22;
stu3.score = 92.5;

结构体整体赋值仅限同类型结构体

结构体类型必须完全一致(包括成员顺序、类型和名称):

struct PointA { int x; int y; };
struct PointB { int x; int y; };

struct PointA a = {1, 2};
struct PointB b = a;// 错误!虽然成员相同,但类型名不同
struct PointA c = a;//正确

结构体成员的访问

普通结构体变量

使用 . 运算符访问成员:

printf("姓名:%s,年龄:%d,分数:%.1f\n", stu1.name, stu1.age, stu1.score);

结构体指针

使用 -> 运算符或 (*ptr). 访问成员:

struct Student *p = &stu1;
printf("姓名:%s\n", p->name);// 等价于 (*p).name
printf("年龄:%d\n", (*p).age);

结构体数组

声明与初始化

struct Student class[3] = {
    {"Alice", 18, 95.5},
    {"Bob", 19, 88.0},
    {"Charlie", 20, 92.5}
};

访问数组成员

for (int i = 0; i < 3; i++) {
    printf("学生%d:%s,%d岁,分数%.1f\n",
        i+1, class[i].name, class[i].age, class[i].score);
}

结构体与指针

指向结构体的指针

struct Student stu = {"David", 21, 90.0};
struct Student *p = &stu;

// 修改成员值
p->age = 22;
strcpy(p->name, "Dave");

动态分配结构体内存

struct Student *p_stu = (struct Student*)malloc(sizeof(struct Student));
if (p_stu != NULL) {
    strcpy(p_stu->name, "Eve");
    p_stu->age = 23;
    p_stu->score = 85.5;
    free(p_stu);// 释放内存
}

结构体作为函数参数

传递结构体副本

函数内修改不影响原结构体:

void print_student(struct Student s) {
    printf("姓名:%s\n", s.name);
}

print_student(stu1);

传递结构体指针

函数内可直接修改原结构体:

void update_age(struct Student *p, int new_age) {
    p->age = new_age;
}

update_age(&stu1, 19);// stu1.age变为19

结构体嵌套

结构体可以包含其他结构体作为成员:

struct Date {
    int year;
    int month;
    int day;
};

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

// 初始化嵌套结构体
struct Person p = {"Tom", {2000, 5, 15}};

// 访问嵌套成员
printf("出生日期:%d年%d月%d日\n",
    p.birthday.year, p.birthday.month, p.birthday.day);

结构体的大小与内存对齐

结构体的大小由成员大小和内存对齐规则决定,可通过 sizeof 获取:

struct Example {
    char c;// 1字节
    int i;// 4字节
    double d;// 8字节
};

printf("结构体大小:%zu字节\n", sizeof(struct Example));// 输出可能是16(对齐到8字节)
注:规则
1.内存按结构体成员的先后顺序排序,当排到该成员时,其前面已开辟的空间字节数必须是该成员类型所占字节数的整数倍
2.整体空间必须是其最大成员类型所占字节的整数倍。
3.根据操作系统:指针(32位系统):4字节;指针(64位系统):8字节
通过 #pragma pack(n) 可强制指定对齐值(n为1、2、4、8等)

常见错误与注意事项

错误 说明 解决方法
未初始化结构体 结构体成员可能包含随机值。 声明时初始化或手动赋值。
结构体赋值浅拷贝 直接赋值结构体时,指针成员指向同一内存。 深拷贝需手动复制指针指向的内容。
混淆.->运算符 对指针使用.或对普通变量使用-> 明确指针用->,普通变量用.
内存对齐浪费空间 结构体成员顺序不合理导致内存浪费。 按成员大小降序排列减少填充。

示例代码

动态创建结构体数组

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Student {
    char name[20];
    int age;
};

int main() {
    int n = 3;
    struct Student *class = (struct Student*)malloc(n * sizeof(struct Student));

    if (class == NULL) {
        printf("内存分配失败!\n");
        return 1;
    }

	// 初始化
	strcpy(class[0].name, "Alice");
    class[0].age = 18;
    strcpy(class[1].name, "Bob");
    class[1].age = 19;

	// 输出
	for (int i = 0; i < n; i++) {
        printf("学生%d:%s,%d岁\n", i+1, class[i].name, class[i].age);
    }

    free(class);
    return 0;
}

结构体与文件操作

#include <stdio.h>
struct Student {
    char name[20];
    int age;
};

int main() {
    FILE *fp = fopen("students.dat", "wb");
    if (fp == NULL) {
        printf("文件打开失败!\n");
        return 1;
    }

    struct Student stu = {"Tom", 20};
    fwrite(&stu, sizeof(struct Student), 1, fp);// 写入文件
    fclose(fp);

	// 读取文件
    fp = fopen("students.dat", "rb");
    struct Student read_stu;
    fread(&read_stu, sizeof(struct Student), 1, fp);
    printf("读取数据:%s,%d岁\n", read_stu.name, read_stu.age);
    fclose(fp);

    return 0;
}
posted @ 2025-09-19 20:57  YouEmbedded  阅读(10)  评论(0)    收藏  举报