在掌握结构体的基本定义与成员访问之后,常需组合使用结构体指针、结构体数组与嵌套结构体,以表达批量数据、动态数量记录以及层次化信息。本节归纳这三类用法。

结构体指针

结构体对象有地址,可定义指向该类型的指针,用 -> 访问成员:

#include <stdio.h>

struct person {
    int age;
    float weight;
};

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

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

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

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

personPtr->age 等价于 (*personPtr).age。

动态分配结构体数组

人数在运行时才确定时,可在堆上分配连续的结构体数组(参见动态内存分配):

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

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

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

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

    ptr = (struct person *)malloc(n * sizeof(struct person));
    if (ptr == NULL) {
        return 1;
    }

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

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

    free(ptr);
    return 0;
}

结构体数组

多个同类型实体可声明为结构体数组,用下标区分各元素:

#include <stdio.h>

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

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

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

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

嵌套结构体

成员类型可以是另一个结构体,用于表示层次数据(如员工与其地址):

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(void) {
    struct employee emp;
    scanf("%19s %19s %d %13s", emp.name, emp.add.city,
          &emp.add.pin, emp.add.phone);

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

嵌套的两种写法

  • 先定义子结构体再嵌入:子类型可在多处复用。
  • 在成员处内嵌定义:写法紧凑,但该子类型通常不能在其他结构体中复用。
struct Date { int dd; int mm; int yyyy; };
struct Employee {
    int id;
    char name[20];
    struct Date doj;
};

struct Employee {
    int id;
    char name[20];
    struct Date { int dd; int mm; int yyyy; } doj;
} e1;

访问嵌套成员时逐层使用点运算符,例如 e1.doj.dd。

结构体作为函数参数

结构体可按值传递给函数,此时复制整个对象;成员也可单独传递。按值传递大结构体开销较高,常改为传递指针(参见按地址传参):

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

结构体指针、数组与嵌套组合使用后,即可组织链表节点、表格记录等较复杂的数据模型;链表等内容见后续链表章节。