结构体:结构体定义、成员访问、结构体数组

结构体,说白了就是让你把一堆不同类型的数据打包成一个整体。我刚开始学C++时,觉得这玩意儿不就是个“大变量”嘛,后来做项目才发现,没有结构体,代码简直没法看。

举个例子,你要描述一个学生。有姓名、年龄、分数。没有结构体时,你得定义三个数组:string names[50]int ages[50]float scores[50]。维护起来?嗯,那叫一个痛苦。有了结构体,一个 Student 类型就搞定了。

结构体的定义

定义结构体,用 struct 关键字。语法很简单:

struct Student {
    string name;
    int age;
    float score;
};  // 别忘了分号!

这里我定义了一个 Student 结构体,里面有三个成员。你可以把它理解成一张“蓝图”,告诉编译器:以后凡是 Student 类型的变量,都长这样。

个人习惯:我一般把结构体定义放在全局区域,或者单独的头文件里。这样多个函数都能用,不用重复写。

结构体变量的声明与初始化

定义好结构体后,就可以声明变量了:

Student stu1;  // 声明一个 Student 变量
Student stu2 = {"张三", 20, 88.5};  // 声明并初始化

注意,初始化时顺序要和定义时一致。我见过有人把年龄和分数写反了,结果一个20岁的小伙子突然变成了88.5岁……

C++11 之后,还支持这种写法:

Student stu3 = {.name = "李四", .age = 22, .score = 91.0};

这种指定初始化方式,可读性更好。我个人比较推荐,尤其是成员很多的时候。

成员访问

访问结构体的成员,用点号 .

stu1.name = "王五";
stu1.age = 19;
stu1.score = 76.5;

cout << stu1.name << " 的分数是 " << stu1.score << endl;

如果是结构体指针,就得用箭头 ->

Student* p = &stu1;
p->age = 21;  // 等价于 (*p).age = 21
我曾经踩过的坑:用指针访问成员时,忘了检查指针是否为 nullptr。结果程序跑着跑着就崩了。后来我养成了习惯:用指针前先判空。

结构体数组

结构体数组,就是数组的每个元素都是一个结构体。处理批量数据时,这玩意儿太好用了。

Student class1[3] = {
    {"赵六", 18, 92.0},
    {"钱七", 19, 85.5},
    {"孙八", 20, 78.0}
};

// 遍历数组
for (int i = 0; i < 3; i++) {
    cout << class1[i].name << " 年龄: " << class1[i].age << endl;
}

你想想看,如果没有结构体,你要维护三个数组,还要保证下标一一对应。新增一个学生?三个数组都得改。有了结构体数组,一个循环就搞定了。

结构体作为函数参数

结构体可以传给函数。但要注意:默认是值传递,会拷贝整个结构体。如果结构体很大,性能会受影响。

void printStudent(Student s) {  // 值传递,拷贝一份
    cout << s.name << endl;
}

void printStudentRef(const Student& s) {  // 引用传递,不拷贝
    cout << s.name << endl;
}

我建议:如果只是读取数据,用 const &。既避免了拷贝,又保证了数据不被修改。

结构体嵌套

结构体里可以嵌套另一个结构体。比如学生有地址信息:

struct Address {
    string city;
    string street;
    int zipCode;
};

struct Student {
    string name;
    int age;
    float score;
    Address addr;  // 嵌套结构体
};

访问嵌套成员:

Student stu;
stu.addr.city = "北京";
stu.addr.zipCode = 100000;

这种嵌套结构,在描述复杂实体时非常自然。我在做学生管理系统时就用了这种设计,数据组织得很清晰。

结构体与内存对齐

嗯,这里要注意一个细节:结构体的大小不一定等于成员大小之和。因为编译器会做内存对齐。

struct Example1 {
    char c;    // 1 字节
    int i;     // 4 字节
};  // 实际大小可能是 8,而不是 5

为什么会这样?因为 CPU 读取对齐的数据更快。编译器会在 char 后面填充 3 个字节,让 int 对齐到 4 字节边界。

避坑指南:如果你要把结构体写入文件或通过网络传输,一定要考虑内存对齐。否则在不同平台间读写数据时,会出现字节错位。我曾经因为这个 bug 排查了整整两天……

知识体系总览

下面这张图,把结构体的核心知识点串起来了:

结构体 定义与初始化 struct Student { ... }; Student stu = {"张三", 20}; 成员访问 点号 . stu.name 箭头 -> p->name 结构体数组 Student class1[3]; 进阶:嵌套 & 内存对齐 嵌套结构体:addr.city 内存对齐:sizeof 可能比预期大

完整示例:学生成绩管理系统

最后,给一个综合例子。把上面讲的知识点都用上:

#include <iostream>
#include <string>
using namespace std;

struct Student {
    string name;
    int age;
    float score;
};

void inputStudent(Student& s) {
    cout << "请输入姓名: ";
    cin >> s.name;
    cout << "请输入年龄: ";
    cin >> s.age;
    cout << "请输入分数: ";
    cin >> s.score;
}

void printStudent(const Student& s) {
    cout << s.name << " | 年龄: " << s.age 
         << " | 分数: " << s.score << endl;
}

int main() {
    Student students[3];
    
    // 输入
    for (int i = 0; i < 3; i++) {
        cout << "第" << i+1 << "个学生:" << endl;
        inputStudent(students[i]);
    }
    
    // 输出
    cout << "\n学生信息如下:" << endl;
    for (int i = 0; i < 3; i++) {
        printStudent(students[i]);
    }
    
    return 0;
}

这个例子虽然简单,但包含了结构体定义、数组、引用传参、const 修饰等核心用法。你可以在它的基础上扩展,比如增加查找、排序功能。

我的建议:刚开始练习时,先写一个结构体,再写一个结构体数组。等熟练了,再尝试嵌套结构体和指针操作。一步一步来,别着急。

公众号:蓝海资料掘金营,微信deep3321