解码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为1248等)

常见错误与注意事项

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

示例代码

动态创建结构体数组
#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值