C语言结构体如何定义与使用?

99ANYc3cd6
预计阅读时长 27 分钟
位置: 首页 C语言 正文

为什么需要结构体?

想象一下,你要描述一个学生,这个学生有姓名、年龄、学号和成绩这些信息,如果用基本的数据类型来表示,你可能需要这样定义:

c语言structure用法
(图片来源网络,侵删)
char name[50];
int age;
int id;
float score;

虽然这样可以,但这些变量是独立的,没有内在的联系,当你需要传递一个学生的所有信息时,你需要传递这四个变量,非常麻烦。

结构体就是为了解决这个问题而生的。 它可以把这些相关的数据项打包成一个整体,就像一个“超级变量”。


结构体的基本定义

结构体的定义使用 struct 关键字,后跟一个结构体名(标签),然后用花括号 包含成员列表。

语法:

struct 结构体名 {
    数据类型 成员1;
    数据类型 成员2;
    数据类型 成员3;
    // ...
};

示例:定义一个学生结构体

// 定义一个名为 Student 的结构体
struct Student {
    char name[50];  // 姓名
    int age;        // 年龄
    int id;         // 学号
    float score;    // 成绩
};

注意:

c语言structure用法
(图片来源网络,侵删)
  • struct Student 是一个完整的类型,就像 intfloat 一样。
  • 定义结构体时,系统并不会为它分配内存,它只是一个“蓝图”或“模板”,只有当你创建该类型的变量时,才会分配内存。

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

定义好结构体类型后,你就可以像使用基本类型一样声明变量了。

1 声明变量

先定义结构体,再声明变量

struct Student {
    char name[50];
    int age;
};
int main() {
    struct Student student1; // 声明一个 Student 类型的变量 student1
    struct Student student2; // 声明另一个变量 student2
    return 0;
}

在定义结构体时直接声明变量(不推荐,因为会使结构体定义和变量声明混在一起)

struct Student {
    char name[50];
    int age;
} student1, student2; // 在这里直接声明了两个变量

2 初始化变量

可以在声明时直接初始化,使用花括号 将初始值列表括起来,顺序必须与结构体成员的顺序一致。

c语言structure用法
(图片来源网络,侵删)
struct Student {
    char name[50];
    int age;
    float score;
};
int main() {
    // 初始化 student1
    struct Student student1 = {"张三", 20, 95.5f};
    // 初始化 student2,部分初始化(后面的成员自动初始化为0)
    struct Student student2 = {"李四", 21};
    return 0;
}

访问结构体成员

使用成员访问运算符(,英文句点)来访问和修改结构体变量的成员。

语法:

结构体变量名.成员名;

示例:

#include <stdio.h>
#include <string.h>
struct Student {
    char name[50];
    int age;
    float score;
};
int main() {
    struct Student student1 = {"张三", 20, 95.5f};
    // 访问成员并打印
    printf("姓名: %s\n", student1.name);
    printf("年龄: %d\n", student1.age);
    printf("成绩: %.2f\n", student1.score);
    // 修改成员的值
    student1.age = 21;
    student1.score = 98.0f;
    printf("\n更新后的信息:\n");
    printf("姓名: %s\n", student1.name);
    printf("年龄: %d\n", student1.age);
    printf("成绩: %.2f\n", student1.score);
    // 给字符串成员赋值,不能直接用 =,要使用 strcpy
    // student1.name = "王五"; // 错误!
    strcpy(student1.name, "王五"); // 正确!
    printf("\n再次更新后的姓名: %s\n", student1.name);
    return 0;
}

结构体数组

你可以创建一个结构体数组,来存储多个相同类型的结构体。

示例:

#include <stdio.h>
struct Student {
    char name[50];
    float score;
};
int main() {
    // 定义一个可以容纳3个 Student 结构体的数组
    struct Student students[3] = {
        {"张三", 95.5},
        {"李四", 88.0},
        {"王五", 92.5}
    };
    // 遍历并打印数组中的每个元素
    for (int i = 0; i < 3; i++) {
        printf("学生 %d: 姓名: %s, 成绩: %.2f\n", i + 1, students[i].name, students[i].score);
    }
    return 0;
}

结构体指针

你可以声明一个指向结构体的指针,通过指针访问成员有两种方式:

  1. (*指针变量名).成员名:先解引用指针,再访问成员。
  2. 指针变量名->成员名:使用箭头运算符 ->(由 和 > 组成),这是更常用、更简洁的方式。

示例:

#include <stdio.h>
#include <string.h>
struct Student {
    char name[50];
    int age;
};
int main() {
    struct Student student1 = {"赵六", 22};
    struct Student *ptr; // 声明一个指向 Student 结构体的指针
    ptr = &student1; // 让 ptr 指向 student1 的地址
    // 使用 . 运算符访问(需要先解引用)
    printf("通过指针和 . 运算符访问: 姓名: %s\n", (*ptr).name);
    // 使用 -> 运算符访问(推荐)
    printf("通过指针和 -> 运算符访问: 年龄: %d\n", ptr->age);
    // 通过指针修改成员
    ptr->age = 23;
    printf("修改后的年龄: %d\n", ptr->age);
    return 0;
}

结构体作为函数参数

结构体可以作为函数的参数传递,主要有两种方式:

1 传递结构体本身(值传递)

这种方式会将整个结构体的内容复制一份传给函数,优点是函数内部不会修改原始数据,保证了安全性,缺点是如果结构体很大,复制会消耗较多时间和内存。

#include <stdio.h>
struct Point {
    int x;
    int y;
};
// 函数接收一个 Point 结构体
void printPoint(struct Point p) {
    printf("Point: (%d, %d)\n", p.x, p.y);
}
int main() {
    struct Point p1 = {10, 20};
    printPoint(p1); // 传递 p1 的一个副本
    return 0;
}

2 传递结构体指针(地址传递)

这种方式只传递结构体的地址,函数通过指针来操作原始结构体,优点是效率高,不涉及数据复制,缺点是函数可能会意外地修改原始数据。

#include <stdio.h>
struct Point {
    int x;
    int y;
};
// 函数接收一个 Point 结构体的指针
void movePoint(struct Point *p, int dx, int dy) {
    p->x += dx; // 通过指针修改原始数据
    p->y += dy;
}
int main() {
    struct Point p1 = {10, 20};
    printf("移动前: (%d, %d)\n", p1.x, p1.y);
    movePoint(&p1, 5, -5); // 传递 p1 的地址
    printf("移动后: (%d, %d)\n", p1.x, p1.y);
    return 0;
}

结构体嵌套

结构体的成员可以是另一个结构体类型,这称为结构体嵌套。

示例:

#include <stdio.h>
// 定义日期结构体
struct Date {
    int year;
    int month;
    int day;
};
// 定义学生结构体,其中包含一个 Date 类型的成员
struct Student {
    char name[50];
    struct Date birthday; // 嵌套结构体
    float score;
};
int main() {
    struct Student student1 = {"钱七", 2000, 5, 20, 91.0f};
    // 访问嵌套结构体的成员
    printf("姓名: %s\n", student1.name);
    printf("生日: %d-%d-%d\n", student1.birthday.year, student1.birthday.month, student1.birthday.day);
    printf("成绩: %.2f\n", student1.score);
    return 0;
}

结构体的大小与内存对齐

sizeof 操作符可以获取结构体的大小,需要注意的是,结构体的大小不一定等于其所有成员大小之和,这涉及到内存对齐问题。

内存对齐的规则(简化版):

  1. 第一个成员放在偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处,对齐数 = 编译器默认的对齐数该成员自身大小 中较小的那个,在VS中,默认对齐数是8;在Linux中,默认是4。
  3. 结构体的总大小,必须是所有成员中最大对齐数的整数倍。
  4. 如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的)的整数倍。

示例:

#include <stdio.h>
struct S1 {
    char c1; // 1字节
    int i;   // 4字节
    char c2; // 1字节
}; // 大小可能是 12 字节 (1 + 3(填充) + 4 + 1 + 3(填充) = 12)
struct S2 {
    int i;   // 4字节
    char c1; // 1字节
    char c2; // 1字节
}; // 大小可能是 8 字节 (4 + 1 + 1 + 2(填充) = 8)
int main() {
    printf("sizeof(struct S1) = %zu\n", sizeof(struct S1));
    printf("sizeof(struct S2) = %zu\n", sizeof(struct S2));
    return 0;
}

输出(在默认对齐数为8的编译器中):

sizeof(struct S1) = 12
sizeof(struct S2) = 8

如何优化结构体大小? 将占用空间小的成员尽量放在一起,可以减少填充字节,从而节省内存。


typedef 与结构体

使用 typedef 可以为结构体类型定义一个别名,让代码更简洁易读。

先定义,再取别名

struct Student {
    char name[50];
    int age;
};
typedef struct Student Student; // 为 struct Student 定义一个别名 Student
int main() {
    // 现在可以直接使用 Student 来声明变量,而不用写 struct Student
    Student s1 = {"孙八", 25};
    printf("姓名: %s\n", s1.name);
    return 0;
}

在定义时直接取别名(更常用)

// 定义结构体的同时,用 typedef 为其取别名
typedef struct {
    char name[50];
    int age;
} Student;
int main() {
    // 直接使用 Student
    Student s1 = {"孙八", 25};
    printf("姓名: %s\n", s1.name);
    return 0;
}

注意: 如果使用方式二,在定义结构体时不能写结构体名(标签),因为 typedef 已经为你创建了一个新类型 Student


功能 语法/示例 描述
定义 struct Student { ... }; 创建一个结构体类型作为“蓝图”。
声明 struct Student s1; 创建一个结构体变量,分配内存。
初始化 struct Student s = {"Tom", 20}; 在声明时赋初值。
访问成员 s.nameptr->name 使用 或 -> 运算符访问/修改成员。
结构体数组 struct Student arr[10]; 存储多个结构体。
结构体指针 struct Student *ptr = &s; 指向结构体变量,通常用于函数传参以提高效率。
函数参数 void func(struct Student s); (值)
void func(struct Student *p); (地址)
决定是复制数据还是传递地址。
嵌套 struct A { struct B b; }; 结构体的成员可以是另一个结构体。
typedef typedef struct { ... } Student; 为结构体类型创建一个简洁的别名。
内存对齐 sizeof(struct S) 结构体大小可能大于成员之和,受对齐规则影响。

掌握结构体是C语言编程的基石,它让你能够更好地组织和操作复杂的数据,是构建更高级数据结构(如链表、树、图)的基础。

-- 展开阅读全文 --
头像
织梦列表页描述字数限制是多少?
« 上一篇 01-22
织梦还原数据能恢复吗?
下一篇 » 01-22
取消
微信二维码
支付宝二维码

目录[+]