C语言:结构体

一,前言

        在我们学习了操作符,选择语句,循环语句,函数以及数组等这些必不可少的知识。那么你是都会好奇,到底学到哪里才算是一个阶段。那我今天就告诉你,C语言结构体学完这个,你就算是把这个初阶的知识学完了。你想的没有错,本篇写完C语言初阶就完结了(后续会更新进阶)。感谢各位大佬的支持,从刚开始写博客没想到会坚持到现在,心情真的很复杂。胡不多说,C语言结构体开讲!!!(本篇50%内容借助ai生成)

一,结构体(struct)

1,结构体的介绍

在C语言中,结构体(struct) 是一种自定义的复合数据类型,允许将多个不同类型的变量组合成一个逻辑单元,类似于现实世界中对象的属性集合。它是C语言实现数据封装和抽象的核心机制之一。

2,结构体定义 

        在C语言中,结构体也是非常重要的一个部分,想要学好结构体,那么先从定义开始!

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

3.用定义去描述一件事物

        假如我想要用结构体描述一本书,我想知道它的价格,多少页,名字是什么?

struct Book 
{
	char BookName[30]; //书的名字
	double Money; //多少钱
	int Page; //页数
	char author //作者
}; //分号不可省略

二,实例化结构体变量

1,直接声明

struct Book myBook; // 声明一个book类型的变量

2,使用typedef简化 

typedef struct 
{
    char title[100];
    char author[50];
    int pages;
    float price;
} Book; 

三,结构体中如何访问成员

#include <stdio.h>
#include <string.h>
// 定义书籍结构体
typedef struct {
    char title[100];  // 书名
    char author[50];  // 作者
    int pages;        // 页数
    float price;      // 价格
} Book;
// 打印书籍信息函数
void print_book_info(const Book* book) {
    printf("===== 书籍信息 =====\n"
        "书名:%s\n"
        "作者:%s\n"
        "页数:%d页\n"
        "价格:%.2f元\n",
        book->title, book->author, book->pages, book->price);
}
int main() {
    // 直接初始化结构体变量
    Book myBook = {
        .title = "结构体指南",
        .author = "Hou'",
        .pages = 99,
        .price = 0.00
    };
    print_book_info(&myBook);
    return 0;
}

四, 内存布局与对齐规则

1,结构体内存布局基本特征

成员按照声明顺序依次存储
每个成员的起始地址都必须满足其类型的对其要求

结构体总大小必须是最大成员对齐值的整数倍

2. 内存布局示意图

struct Example 
{
    char a;      // 1字节
    int b;       // 4字节
    short c;     // 2字节
    double d;    // 8字节
};

内存布局(假设8字节对齐) 

| a | 填充3字节 | b (4字节) | c | 填充6字节 | d (8字节) |
| 0   1   2   3 | 4   5   6   7 | 8   9 | 10-15      | 16-23     |

3、对齐规则详解 

3.1.对齐系数

基本类型对齐值 = 类型大小
char : 1字节
short : 2字节
int : 4字节
double : 8字节
结构体对齐值 = 类型大小

2. 偏移量计算规则 

注:每个成员的编译地址必须满足:
offset % alignment == 0

3. 结构体总大小计算 

计算累计代下(包含填充字节)
最终大小必须时最大对齐值得整数倍

四、内存对齐的本质原因 (仅做了解)

硬件优化缓存行优化原子操作支持
CPU访问对齐内存效率更高现在CPU缓存行通常为64字节原子操作必须对齐(看情况)
某些架构无法直接访问非对齐内存对齐数据可以最大化缓存利用率

五,结构体的优势

1. 数据封装与逻辑组织

优势:将多个相关数据项组合为单一实体,形成有意义的抽象数据类型。
示例:管理学生信息时,无需使用多个独立变量,而是组合成一个逻辑单元。

// 未使用结构体(松散变量)
char studentName[50];
int studentAge;
float studentGPA;
char studentMajor[30];
// 使用结构体(封装数据)
struct Student 
{
    char name[50];
    int age;
    float gpa;
    char major[30];
};

 2. 简化函数参数传递

优势:通过传递结构体指针,减少函数参数数量,提升代码可维护性。

// 未使用结构体:需传递多个参数
void printStudent(char *name, int age, float gpa) 
{
    printf("Name: %s, Age: %d, GPA: %.2f\n", name, age, gpa);
}

// 使用结构体:只需一个指针参数
void printStudent(struct Student *s)
 {
    printf("Name: %s, Age: %d, GPA: %.2f\n", s->name, s->age, s->gpa);
}

3. 高效内存管理

优势:结构体成员在内存中连续存储,提升缓存利用率,适合批量处理数据。

// 创建结构体数组
struct Student class[50];
// 连续访问内存(高效率)
for (int i = 0; i < 50; i++) 
{
    class[i].age = 18 + i;
}
对比松散变量:
int ages[50];
char names[50][50];
// 内存不连续,缓存命中率低

 4. 增强代码可读性与维护性

优势:通过有意义的类型命名,使代码自文档化。

// 未使用结构体(含义不明确)
void processData(int x, int y, int width, int height);
// 使用结构体(语义清晰)
struct Rectangle 
{
    int x;
    int y;
    int width;
    int height;
};
void processRect(struct Rectangle *rect);

六,结构体高级用法

1. 动态内存分配

typedef struct TreeNode 
{
    int data;
    struct TreeNode *left;
    struct TreeNode *right;
} TreeNode;
// 创建树节点
TreeNode* createNode(int val) 
{
    TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));
    if(newNode) 
{
        newNode->data = val;
        newNode->left = newNode->right = NULL;
    }
    return newNode;
}
// 插入二叉搜索树
void insertBST(TreeNode** root, int val) 
{
    if(!*root) 
{
        *root = createNode(val);
        return;
    }
    if(val < (*root)->data)
        insertBST(&(*root)->left, val);
    else
        insertBST(&(*root)->right, val);
}

2. 自引用结构体(链表)

struct ListNode 
{
    int val;
    struct ListNode* next;
    struct ListNode* random;  // 复杂链表
};

// 深拷贝带随机指针的链表
struct ListNode* copyRandomList(struct ListNode* head)
 {
    // ...实现省略...
}

3. 实现类面向对象编程 

typedef struct 
{
    int x, y;
    void (*draw)(void* self);  // 函数指针
    void (*move)(void* self, int dx, int dy);
} Shape;

// 圆形实现
void circleDraw(void* self)
 {
    Shape* s = (Shape*)self;
    printf("Drawing circle at (%d,%d)\n", s->x, s->y);
}

void circleMove(void* self, int dx, int dy)
 {
    Shape* s = (Shape*)self;
    s->x += dx;
    s->y += dy;
}

// 创建对象
Shape createCircle(int x, int y) 
{
    return (Shape){x, y, circleDraw, circleMove};
}

// 使用
Shape c = createCircle(10,20);
c.draw(&c);
c.move(&c,5,5);

七,结构体与函数

1. 值传递 vs 指针传递

typedef struct 
{
    int x;
    int y;
} Point;

// 值传递(复制整个结构体)
void movePointVal(Point p, int dx, int dy)
 {
    p.x += dx;
    p.y += dy; // 修改不影响原结构体
}

// 指针传递(推荐方式)
void movePointPtr(Point* p, int dx, int dy) 
{
    p->x += dx;
    p->y += dy; // 直接影响原结构体
}

int main() {
    Point pt = {10, 20};
    movePointVal(pt, 5, 5);    // pt仍为(10,20)
    movePointPtr(&pt, 5, 5);   // pt变为(15,25)
    return 0;
}

八,学生管理系统

学生管理系统得实现和功能

1,数据结构:
使用链表结构存储学生数据
学生信息包含:学号、姓名、年龄、成绩
2,主要功能:
添加学生信息(自动检测学号重复)
删除学生信息(按学号)
修改学生信息(按学号)
查询学生(支持学号/姓名查询)
显示所有学生信息(表格形式)
数据持久化(二进制文件存储)
3,特色功能:
输入合法性检测
内存自动管理
友好的用户界面
数据文件自动加载/保存
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX_NAME_LEN 50
#define MAX_ID_LEN 15
#define DATA_FILE "students.dat"

typedef struct Student 
{
    char id[MAX_ID_LEN];      // 学号
    char name[MAX_NAME_LEN];  // 姓名
    int age;                  // 年龄
    float score;              // 成绩
    struct Student *next;     // 链表指针
} Student;

Student *head = NULL;  // 全局链表头指针

/* 函数声明 */
void display_menu();
void add_student();
void delete_student();
void modify_student();
void search_student();
void display_all();
void save_to_file();
void load_from_file();
void clear_list();
int is_id_exist(const char *id);
Student *create_node();
void press_enter_to_continue();

int main() 

{
    load_from_file();
    
    int choice;
    do {
        display_menu();
        printf("请输入选项:");
        scanf("%d", &choice);
        while(getchar() != '\n'); // 清空输入缓冲区
        
        switch(choice)
 {
            case 1: add_student(); break;
            case 2: delete_student(); break;
            case 3: modify_student(); break;
            case 4: search_student(); break;
            case 5: display_all(); break;
            case 6: save_to_file(); break;
            case 0: printf("感谢使用!\n"); break;
            default: printf("无效选项,请重新输入!\n");
        }
    } while(choice != 0);

    clear_list();
    return 0;
}

void display_menu() 
{
    printf("\n===== 学生管理系统 =====\n");
    printf("1. 添加学生\n");
    printf("2. 删除学生\n");
    printf("3. 修改信息\n");
    printf("4. 查询学生\n");
    printf("5. 显示所有学生\n");
    printf("6. 保存数据\n");
    printf("0. 退出系统\n");
    printf("=========================\n");
}

Student *create_node() 
{
    Student *new_node = (Student *)malloc(sizeof(Student));
    if(new_node == NULL) 
{
        printf("内存分配失败!\n");
        exit(EXIT_FAILURE);
    }
    new_node->next = NULL;
    return new_node;
}

void add_student() 
{
    Student *new_node = create_node();
    
    printf("\n--- 添加学生 ---\n");
    do {
        printf("请输入学号:");
        fgets(new_node->id, MAX_ID_LEN, stdin);
        new_node->id[strcspn(new_node->id, "\n")] = '\0'; // 去除换行符
    } while(is_id_exist(new_node->id));

    printf("请输入姓名:");
    fgets(new_node->name, MAX_NAME_LEN, stdin);
    new_node->name[strcspn(new_node->name, "\n")] = '\0';

    printf("请输入年龄:");
    scanf("%d", &new_node->age);

    printf("请输入成绩:");
    scanf("%f", &new_node->age);

    // 头插法添加到链表
    new_node->next = head;
    head = new_node;
    
    printf("添加成功!\n");
    press_enter_to_continue();
}

void delete_student()
 {
    if(head == NULL)
 {
        printf("当前没有学生记录!\n");
        return;
    }

    char target_id[MAX_ID_LEN];
    printf("\n--- 删除学生 ---\n");
    printf("请输入要删除的学号:");
    fgets(target_id, MAX_ID_LEN, stdin);
    target_id[strcspn(target_id, "\n")] = '\0';

    Student *current = head, *prev = NULL;
    while(current != NULL) 
{
        if(strcmp(current->id, target_id) == 0) 
{
            if(prev == NULL)
 {
                head = current->next;
            } else 
{
                prev->next = current->next;
            }
            free(current);
            printf("删除成功!\n");
            press_enter_to_continue();
            return;
        }
        prev = current;
        current = current->next;
    }
    printf("未找到该学号的学生!\n");
    press_enter_to_continue();
}

void modify_student() 
{
    if(head == NULL) 
{
        printf("当前没有学生记录!\n");
        return;
    }

    char target_id[MAX_ID_LEN];
    printf("\n--- 修改信息 ---\n");
    printf("请输入要修改的学号:");
    fgets(target_id, MAX_ID_LEN, stdin);
    target_id[strcspn(target_id, "\n")] = '\0';

    Student *current = head;
    while(current != NULL) 
{
        if(strcmp(current->id, target_id) == 0) {
            printf("原姓名:%s\n新姓名:", current->name);
            fgets(current->name, MAX_NAME_LEN, stdin);
            current->name[strcspn(current->name, "\n")] = '\0';

            printf("原年龄:%d\n新年龄:", current->age);
            scanf("%d", &current->age);

            printf("原成绩:%.1f\n新成绩:", current->score);
            scanf("%f", &current->score);

            printf("修改成功!\n");
            press_enter_to_continue();
            return;
        }
        current = current->next;
    }
    printf("未找到该学号的学生!\n");
    press_enter_to_continue();
}

void search_student() 
{
    if(head == NULL) 
{
        printf("当前没有学生记录!\n");
        return;
    }

    char target[MAX_ID_LEN];
    printf("\n--- 查询学生 ---\n");
    printf("请输入学号或姓名:");
    fgets(target, MAX_ID_LEN, stdin);
    target[strcspn(target, "\n")] = '\0';

    Student *current = head;
    int found = 0;
    while(current != NULL)
{
        if(strcmp(current->id, target) == 0 || 
           strcmp(current->name, target) == 0) 
{
            printf("\n学号:%s\n", current->id);
            printf("姓名:%s\n", current->name);
            printf("年龄:%d\n", current->age);
            printf("成绩:%.1f\n\n", current->score);
            found = 1;
        }
        current = current->next;
    }

    if(!found) 
{
        printf("未找到匹配的学生!\n");
    }
    press_enter_to_continue();
}

void display_all() 
{
    if(head == NULL) 
{
        printf("当前没有学生记录!\n");
        return;
    }

    printf("\n%-15s%-20s%-8s%-8s\n", 
           "学号", "姓名", "年龄", "成绩");
    printf("----------------------------------------\n");
    
    Student *current = head;
    while(current != NULL)
 {
        printf("%-15s%-20s%-8d%-8.1f\n", 
               current->id, 
               current->name,
               current->age,
               current->score);
        current = current->next;
    }
    press_enter_to_continue();
}

void save_to_file()
 {
    FILE *fp = fopen(DATA_FILE, "wb");
    if(fp == NULL) 
{
        printf("无法打开文件进行保存!\n");
        return;
    }

    Student *current = head;
    while(current != NULL) 
{
        fwrite(current, sizeof(Student), 1, fp);
        current = current->next;
    }

    fclose(fp);
    printf("数据已保存到%s\n", DATA_FILE);
    press_enter_to_continue();
}

void load_from_file()
 {
    FILE *fp = fopen(DATA_FILE, "rb");
    if(fp == NULL) return;

    Student temp, *new_node;
    while(fread(&temp, sizeof(Student), 1, fp) == 1)
 {
        new_node = create_node();
        memcpy(new_node, &temp, sizeof(Student));
        new_node->next = head;
        head = new_node;
    }

    fclose(fp);
}

void clear_list() 
{
    Student *current = head;
    while(current != NULL) 
{
        Student *temp = current;
        current = current->next;
        free(temp);
    }
    head = NULL;
}

int is_id_exist(const char *id)
 {
    Student *current = head;
    while(current != NULL) 
{
        if(strcmp(current->id, id) == 0) 
{
            printf("该学号已存在!\n");
            return 1;
        }
        current = current->next;
    }
    return 0;
}

void press_enter_to_continue()
 {
    printf("\n按回车键继续...");
    while(getchar() != '\n');
    getchar(); // 等待回车
}

十,结构体

        以下是一份系统完整的C语言结构体综合示例代码,涵盖结构体核心用法、内存管理、高级特性及实际应用

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>  // 用于offsetof宏
/*-----------------------------------------
  第一部分:结构体基础与内存布局
-----------------------------------------*/
// 基础结构体定义
typedef struct
{
    char id[10];
    int age;
    double score;
} Student;
// 带内存对齐控制的结构体
#pragma pack(push, 1)  // 1字节对齐
typedef struct 
{
    char type;
    int code;
    short flag;
} PackedData;
#pragma pack(pop)      // 恢复默认对齐
void basic_demo()
 {
    printf("\n=== 结构体基础与内存布局演示 ===\n");
    
    Student s1 = {"A1001", 20, 85.5};
    printf("学生 %s 年龄:%d 分数:%.1f\n", s1.id, s1.age, s1.score);
    
    printf("默认对齐结构体大小:%zu\n", sizeof(Student));
    printf("紧凑对齐结构体大小:%zu\n", sizeof(PackedData));
}

/*-----------------------------------------
  第二部分:结构体高级特性
-----------------------------------------*/
// 位域结构体
typedef struct 
{
    unsigned int is_active : 1;
    unsigned int mode : 3;
    unsigned int : 4;     // 未命名位域
    unsigned int value : 8;
} StatusRegister;

// 包含函数指针的结构体
typedef struct 
{
    double x, y;
    void (*print)(const struct Vector*);
} Vector;

void vector_print(const Vector* v) 
{
    printf("(%.2f, %.2f)\n", v->x, v->y);
}

// 柔性数组成员
typedef struct 
{
    size_t length;
    int data[];
} IntArray;

IntArray* create_int_array(size_t len) 
{
    IntArray* arr = malloc(sizeof(IntArray) + len * sizeof(int));
    if(arr) 
{
        arr->length = len;
        for(size_t i=0; i<len; i++) 
            arr->data[i] = (int)(i+1);
    }
    return arr;
}

void advanced_demo()
 {
    printf("\n=== 高级特性演示 ===\n");
    
    // 位域操作
    StatusRegister reg = {1, 5, 128};
    printf("状态寄存器:active=%d mode=%d value=%d\n", 
           reg.is_active, reg.mode, reg.value);
    
    // 函数指针成员
    Vector v = {3.5, 4.2, vector_print};
    printf("向量坐标:");
    v.print(&v);
    
    // 柔性数组使用
    IntArray* arr = create_int_array(5);
    printf("动态数组:");
    for(size_t i=0; i<arr->length; i++)
        printf("%d ", arr->data[i]);
    free(arr);
}

/*-----------------------------------------
  第三部分:结构体与函数
-----------------------------------------*/
// 结构体作为函数参数
void update_student(Student* s, int new_age) 
{
    s->age = new_age;
    s->score *= 1.1;
}

// 返回结构体的函数
Student create_student(const char* id, int age) 
{
    Student s;
    strncpy(s.id, id, sizeof(s.id)-1);
    s.age = age;
    s.score = 60.0;
    return s;
}

// 回调函数示例
typedef struct 
{
    int value;
    void (*callback)(int);
} Event;

void event_handler(int val) 
{
    printf("事件触发,值:%d\n", val);
}

void function_demo() {
    printf("\n=== 结构体与函数交互演示 ===\n");
    
    Student s2 = create_student("B2002", 19);
    update_student(&s2, 20);
    printf("更新后学生:%s 年龄:%d\n", s2.id, s2.age);
    
    Event e = {100, event_handler};
    if(e.callback) e.callback(e.value);
}

/*-----------------------------------------
  第四部分:链表数据结构实现
-----------------------------------------*/
typedef struct Node {
    int data;
    struct Node* next;
} ListNode;

ListNode* create_list(int values[], int n) {
    ListNode head = {0, NULL};
    ListNode* current = &head;
    
    for(int i=0; i<n; i++) {
        current->next = malloc(sizeof(ListNode));
        current = current->next;
        current->data = values[i];
        current->next = NULL;
    }
    return head.next;
}

void list_demo() 
{
    printf("\n=== 链表结构演示 ===\n");
    int values[] = {10, 20, 30, 40};
    ListNode* list = create_list(values, 4);
    
    ListNode* current = list;
    while(current)
 {
        printf("%d -> ", current->data);
        current = current->next;
    }
    printf("NULL\n");
    
    // 内存释放(实际开发需实现)
}

/*-----------------------------------------
  第五部分:内存对齐验证
-----------------------------------------*/
void alignment_check() 
{
    printf("\n=== 内存对齐验证 ===\n");
    printf("Student结构体成员偏移:\n");
    printf("id偏移:%zu\n", offsetof(Student, id));
    printf("age偏移:%zu\n", offsetof(Student, age));
    printf("score偏移:%zu\n", offsetof(Student, score));
    
    // 静态断言(C11)
    _Static_assert(sizeof(Student) == 24, "Student结构体大小异常");
}

int main()
 {
    basic_demo();
    advanced_demo();
    function_demo();
    list_demo();
    alignment_check();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值