结构体单独用已经很方便,但真正形成战斗力的是三种组合:结构体指针、结构体数组、嵌套结构体。下面一次讲完这三样。

结构体指针:用 -> 访问成员

结构体变量有地址,自然也有指向它的指针:

struct name {
    member1;
    member2;
};

int main() {
    struct name *ptr, Harry;
}

这里 ptr 是指向 struct name 的指针。用指针访问成员时用箭头运算符 ->

#include <stdio.h>

struct person {
    int age;
    float weight;
};

int main() {
    struct person *personPtr, person1;
    personPtr = &person1;

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

    printf("输入体重: ");
    scanf("%f", &personPtr->weight);

    printf("显示:\n");
    printf("年龄: %d\n", personPtr->age);
    printf("体重: %f", personPtr->weight);
    return 0;
}

输出:

输入年龄: 25
输入体重: 55
显示:
年龄: 25
体重: 55.000000

personPtr->age 完全等价于 (*personPtr).age,先解引用拿到结构体,再用点访问成员。箭头只是更简洁的写法。

动态分配结构体数组

人数不确定时用 malloc 动态分配一批结构体,然后靠 (ptr+i)->xxx 逐条访问:

#include <stdio.h>
#include <stdlib.h>

struct person {
    int age;
    float weight;
    char name[30];
};

int main() {
    struct person *ptr;
    int i, n;

    printf("输入人数: ");
    scanf("%d", &n);

    // 为 n 个 struct person 分配内存
    ptr = (struct person *)malloc(n * sizeof(struct person));

    for (i = 0; i < n; ++i) {
        printf("分别输入姓名和年龄: ");
        scanf("%s %d", (ptr + i)->name, &(ptr + i)->age);
    }

    printf("显示信息:\n");
    for (i = 0; i < n; ++i)
        printf("姓名: %s\t年龄: %d\n", (ptr + i)->name, (ptr + i)->age);

    free(ptr);
    return 0;
}

先为 n 个结构体分配一块连续内存,再用指针算术访问第 i 个元素,这和数组操作是一回事。

结构体数组:批量存同类实体

要存 5 个学生,声明 5 个变量 s1..s5 会写到手抽筋。直接声明一个结构体数组:

#include <stdio.h>
#include <string.h>

struct student {
    int rollno;
    char name[10];
};

int main() {
    int i;
    struct student st[5];

    printf("输入5个学生数据");
    for (i = 0; i < 5; i++) {
        printf("\n输入序号: ");
        scanf("%d", &st[i].rollno);
        printf("\n输入姓名: ");
        scanf("%s", &st[i].name);
    }

    printf("\n学生信息列表:");
    for (i = 0; i < 5; i++) {
        printf("\n序号:%d, 姓名:%s", st[i].rollno, st[i].name);
    }
    return 0;
}

输出:

输入5个学生数据
输入序号:1
输入姓名:Sonoo
输入序号:2
输入姓名:Ratan
...
学生信息列表:
序号:1, 姓名:Sonoo
序号:2, 姓名:Ratan
...

结构体数组就是一组同类型的结构体变量,每个元素存不同实体的信息,配合下标 st[i] 访问成员。

嵌套结构体:结构体里的结构体

一个结构体的成员可以是另一个结构体。比如员工有地址,地址又包含城市、邮编、电话,把地址做成单独结构体,再嵌进员工:

struct address {
    char city[20];
    int pin;
    char phone[14];
};

struct employee {
    char name[20];
    struct address add;   // 嵌套地址结构体
};

访问嵌套成员用练级式的点:外层.内层.成员:

#include <stdio.h>

struct address {
    char city[20];
    int pin;
    char phone[14];
};

struct employee {
    char name[20];
    struct address add;
};

int main() {
    struct employee emp;
    printf("输入员工信息?\n");
    scanf("%s %s %d %s", emp.name, emp.add.city,
          &emp.add.pin, emp.add.phone);

    printf("打印员工信息....\n");
    printf("姓名: %s\n城市: %s\n编号: %d\n电话: %s\n",
           emp.name, emp.add.city, emp.add.pin, emp.add.phone);
    return 0;
}

输出:

输入员工信息?
Arun
Delhi
110001
1234567890
打印员工信息....
姓名: Arun
城市: Delhi
编号: 110001
电话: 1234567890

两种嵌套方式

  • 分开定义结构体再嵌套:先写 struct Date { ... },再在 Employee 里放 struct Date doj;。好处是 Date 可以被多个结构体复用。
  • 内嵌声明:直接在结构体内部声明嵌套结构体,代码紧凑,但不能被其他结构体复用。
/* 方式一:分开定义(可复用) */
struct Date { int dd; int mm; int yyyy; };
struct Employee {
    int id;
    char name[20];
    struct Date doj;
} emp1;

/* 方式二:内嵌声明(紧凑但不可复用) */
struct Employee {
    int id;
    char name[20];
    struct Date {
        int dd; int mm; int yyyy;
    } doj;
} e1;

访问嵌套成员就是一层层点下去:e1.doj.dde1.doj.mme1.doj.yyyy

结构体也能传给函数

结构体可以整体传给函数(按值拷贝一份),成员也可以单独传:

void display(struct employee emp) {
    printf("%s %s %d %s", emp.name, emp.add.city,
           emp.add.pin, emp.add.phone);
}

注意按值传递会整个拷贝结构体,结构体很大时开销不小,想省内存可以传指针(const struct employee *),见函数指针按引用传参相关章节。

指针配合 -> 动态管理结构体,数组批量存储,嵌套表达层级数据,这三样组合起来,结构体才真正能组织起复杂数据。