C 语言 - 结构体(struct)解析

struct 是什么

  • struct 就是一种“自定义数据类型”,可以把多个不同类型的变量打包在一起,组成一个整体,表示定义一个结构体类型。

举个通俗例子

  • 假设保存一个“学生”的信息,有三个内容:

    • 姓名(字符串)
    • 年龄(整数)
    • 成绩(浮点数)
  • 如果不用 struct,你得写三个独立变量:

    char name[20]; // 存放姓名的字符数组,最多能存放 19 个字符 + 1 个终止符 '\0'
    //在 C 语言中,char name[20] 这个数组最多只能存 19 个字符,最后一个位置必须留给字符串结束符 '\0'
    int age; // 年龄
    float score; //成绩
    
  • 如果有 300 个学生就要 600 个变量,既不方便传递,也不方便管理。这时候就可以使用 struct 打包成一个“学生类型”

    • 假设 1 个学生

      struct Student { //定义了一个结构体类型,叫 struct Student
          //里面包含 3 个“成员变量”
          char name[20]; 
          int age; 
          float score; 
      };
      
      //使用方式
      struct Student s1 = {"Alice", 18, 95.5};
      //现在 s1 就是一个“学生对象”,里面同时有名字、年龄、成绩三样东西。
      
      //访问方式
      printf("%s 的年龄是 %d,成绩是 %.1f\n", s1.name, s1.age, s1.score);
      
    • 假设 300 个学生

      #include <stdio.h>
      #include <stdlib.h>
      
      struct Student {
          char name[20];
          int age;
          float score;
      };
      
      int main() {
          int n;
          printf("请输入学生人数(最多300人):");
          scanf("%d", &n);
      
          if (n <= 0 || n > 300) {
              printf("人数无效!\n");
              return 1;
          }
      
          // 定义结构体数组(最多300人)
          // 将定义好的结构体类型 struct Student,作为一种数据类型,用它创建一个有 300 个成员的数组变量 students
          struct Student students[300];
      
          // 输入学生信息
          for (int i = 0; i < n; i++) {
              printf("请输入第 %d 个学生的 姓名 年龄 成绩:", i + 1);
              scanf("%s %d %f", students[i].name, &students[i].age, &students[i].score);
          }
      
          // 输出学生信息并计算平均成绩
          float sum = 0;
          printf("\n------ 学生信息列表 ------\n");
          for (int i = 0; i < n; i++) {
              printf("%-10s 年龄: %2d  成绩: %.1f\n",
                     students[i].name, students[i].age, students[i].score);
              sum += students[i].score;
          }
      
          printf("----------------------------\n");
          printf("平均成绩:%.2f\n", sum / n);
      
          return 0;
      }
      
  • 在内存中表示为:

    偏移地址 内容 类型 大小
    +0 name[20] char[20] 20字节
    +20 age int 4字节
    +24 score float 4字节
    总计 28字节(单个字节最大为4,对齐后可能是28+4=32字节)
    • 也就是说,一个结构体是一块连续的内存区域,里面按顺序放着它的每个成员。

结构体的局限性

不能有成员函数

  • 只能放数据,不能直接放行为(函数),想让结构体“会做事情”,只能用函数指针或者写单独的函数

    struct Person {
        char name[20];
        int age;
        void (*sayHello)(void);  // 可以存函数指针,但不是真正的成员函数
    };
    

没有继承机制

  • 结构体不能继承其他结构体,想模拟“继承”,只能把父结构体放在子结构体里作为成员

    struct A {
        int x;
    };
    
    struct B {
        struct A parent;  // 模拟继承(B的成员继承A的成员)
        int y;
    };
    

没有访问控制(封装)

  • 结构体成员全都是公开的,没有 private / protected / public,外部代码可以直接访问任意成员。

不支持多态

  • 结构体不能像 C++ 类一样通过虚函数实现多态(C语言结构体没有“变身能力”,不同结构体不能表现出不同行为),如果想用“多态”,只能自己写函数指针表(vtable)来模拟,非常麻烦

    #include <stdio.h>
    
    // 定义结构体类型
    struct Animal {
        // 声明一个函数指针,用于保存“发声”函数的地址
        void (*speak)(void);
    };
    
    // 定义不同动物各自的发声函数
    void dogSpeak() { printf("汪汪\n"); }
    void catSpeak() { printf("喵喵\n"); }
    
    int main() {
        // 创建结构体变量,并让函数指针分别指向不同的发声函数
        struct Animal dog = { dogSpeak };
        struct Animal cat = { catSpeak };
    
        // 模拟面向对象的多态效果
        dog.speak(); // 汪汪
        cat.speak(); // 喵喵
    
        // 虽然可以实现,但不像 C++/Java 那样天然支持多态
        return 0;
    }
    

不能直接返回抽象对象

  • C语言结构体只能返回值或者指针

    #include <stdio.h>    // 标准输入输出(若要打印调试信息时需要)
    #include <stdlib.h>   // malloc、free 等需要的头文件
    #include <string.h>   // strcpy/strncpy 等字符串函数需要的头文件
    
    //返回值
    // 定义一个结构体类型 Person,包含 name 和 age 两个成员
    struct Person {
        char name[20];  // 存放姓名的字符数组,最多能存放 19 个字符 + 1 个终止符 '\0'
        int age;        // 年龄
    };
    
    // 定义一个函数,返回 struct Person 的“值”(按值返回)
    struct Person createPerson() {
        struct Person p;                  // 在栈上创建一个“人”变量 p(临时工)
        strcpy(p.name, "张三");           // 给 p 的名字字段赋值
        p.age = 20;                       // 给 p 的年龄字段赋值
        return p;                         // 把整个“人”复制一份返回(返回的是副本)
    }
    //函数里临时造了一个“张三”,然后复制一份“张三”的资料带出去。
    //函数结束后,里面的临时变量会消失,但返回的那份副本还在外面
    // 按值返回是“复制一份交出去”;
    =============================================================
        
    // 返回指针版本
    // 定义一个函数:在“堆”上创建一个 Person,并返回它的地址
    struct Person* createPersonPtr() {
        // 在堆上分配一块内存,用来放一个 struct Person
        struct Person* p = malloc(sizeof(struct Person));
        
        // 判断是否分配成功(非常重要的安全检查)
        if (p == NULL) {
            return NULL;  // 如果分配失败,就返回空指针,让调用者自己判断
        }
    
        // 给结构体中的 name 字段赋值
        // “->” 是访问指针所指结构体成员的运算符,相当于 (*p).name
        strcpy(p->name, "李四");
    
        // 给结构体中的 age 字段赋值
        p->age = 25;  // “->” 的优先级比点高,所以 p->age 相当于 (*p).age
    
        // 返回在堆上创建的这个“人”的地址
        // 调用者要记得使用完后 free(p),否则会造成内存泄漏
        return p;
    }
    // 返回指针是“告诉你原件在哪”
    
  • 没有类的抽象和接口,所以高级封装和抽象都要靠函数 + 指针手动实现,就像是模拟多态

    #include <stdio.h>
    
    struct Animal {
        void (*speak)(void);
    };
    
    void dogSpeak() { printf("汪汪\n"); }
    void catSpeak() { printf("喵喵\n"); }
    
    int main() {
        struct Animal dog = { dogSpeak };
        struct Animal cat = { catSpeak };
        
        dog.speak(); // 输出 汪汪
        cat.speak(); // 输出 喵喵
    }
    

C/C++结构体对比

特性 C语言结构体 C++ 类
数据成员
成员函数 ❌(只能用函数指针模拟)
继承
多态 ❌(只能手动模拟)
封装
抽象类/接口

总结

用途 说明
打包数据 把多个不同类型的变量组合成一个整体
自定义类型 可创建新的“复合类型”供多处复用
便于传参与管理 可以整体传给函数或数组中存储
  • structC 语言中用来创建“自定义复合类型”的修饰符,能把多种不同类型的变量打包成一个整体,让代码更清晰可扩展
posted @ 2025-10-15 12:06  阿俊学编程  阅读(60)  评论(0)    收藏  举报