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

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

什么是结构体?

在 C 语言中,结构体 是一种用户自定义的数据类型,它允许你将不同类型的变量组合成一个单一的、有意义的整体。

c语言 结构 structure
(图片来源网络,侵删)

打个比方:

  • 一个 int 类型只能存储一个整数。
  • 一个 float 类型只能存储一个浮点数。
  • 但一个“学生”的信息,包含了姓名(字符串)、年龄(整数)、成绩(浮点数)等多个不同类型的数据。

结构体就相当于一个“模板”,我们可以用它来创建“学生”这样的数据类型,把学生的所有信息都打包在一起。

为什么需要结构体?

  1. 数据组织:将相关联的数据项组合在一起,使代码更有条理,更易于理解。
  2. 数据传递:当需要传递一个复杂对象的所有数据时,传递一个结构体变量比传递多个独立的参数要方便得多。
  3. 创建复杂数据结构:结构体是构建链表、树、图等复杂数据结构的基础。

如何定义和声明结构体?

1 定义结构体类型

使用 struct 关键字来定义一个结构体,定义只是创建了一个“蓝图”,并不会分配内存。

c语言 结构 structure
(图片来源网络,侵删)
struct Student {
    char name[50]; // 成员:姓名
    int age;       // 成员:年龄
    float score;   // 成员:成绩
};
  • struct Student 是这个新类型的名称。
  • name, age, score 是结构体的成员字段
  • 注意:定义的末尾需要一个分号 ,这是一个非常常见的初学者错误点。

2 声明结构体变量

定义好类型后,我们就可以像使用 int, char 一样来声明变量了。

先定义类型,再声明变量

struct Student {
    char name[50];
    int age;
    float score;
};
// 声明两个 Student 类型的变量
struct Student student1;
struct Student student2;

在定义类型的同时声明变量

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

使用 typedef 创建别名(强烈推荐)

c语言 结构 structure
(图片来源网络,侵删)

typedef 可以为一个已有的类型(包括我们自定义的结构体)创建一个新的名称,这能让代码更简洁、更易读。

// 定义结构体 Student 并为其创建别名 Stu
typedef struct {
    char name[50];
    int age;
    float score;
} Stu;
// 现在可以直接使用 Stu 来声明变量,而无需 struct Student
Stu student1;
Stu student2;

最佳实践:在 C 语言中,使用 typedef 为结构体创建别名是一种非常普遍且推荐的做法。


如何访问和初始化结构体成员?

使用成员访问运算符(也叫“点运算符”) 来访问结构体变量的成员。

1 初始化

#include <stdio.h>
#include <string.h>
typedef struct {
    char name[50];
    int age;
    float score;
} Stu;
int main() {
    // 声明并初始化
    Stu student1 = {"张三", 18, 95.5f};
    // 先声明,后逐个赋值
    Stu student2;
    strcpy(student2.name, "李四"); // 字符串不能直接用 = 赋值,需要 strcpy
    student2.age = 19;
    student2.score = 88.0f;
    // 访问并打印成员
    printf("学生1: 姓名=%s, 年龄=%d, 成绩=%.1f\n", student1.name, student1.age, student1.score);
    printf("学生2: 姓名=%s, 年龄=%d, 成绩=%.1f\n", student2.name, student2.age, student2.score);
    return 0;
}

2 访问和修改成员

// 修改 student1 的成绩
student1.score = 98.0f;
// 访问成员
printf("%s 的最新成绩是: %f\n", student1.name, student1.score);

结构体的其他重要特性

1 结构体数组

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

Stu class[5]; // 创建一个可以存放5个学生的数组
// 初始化数组
Stu class[5] = {
    {"张三", 18, 95.5},
    {"李四", 19, 88.0},
    {"王五", 18, 92.5},
    {"赵六", 20, 76.5},
    {"钱七", 19, 85.0}
};
// 访问数组中的元素
printf("第二个学生是: %s\n", class[1].name);

2 结构体指针

可以声明一个指向结构体的指针。

Stu student = {"王五", 18, 92.5f};
Stu *ptr = &student; // ptr 指向 student 变量的地址
// 通过指针访问成员
// 方式1: 使用解引用运算符 * 和点运算符 .
printf("姓名: %s\n", (*ptr).name);
// 方式2: 使用结构体指针成员访问运算符 ->
// 这是更常用、更简洁的方式
printf("年龄: %d\n", ptr->age);
printf("成绩: %.1f\n", ptr->score);

-> 和 的区别

  • 用于结构体变量
  • ->:用于结构体指针

3 结构体作为函数参数

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

  1. 传值:将整个结构体的副本传递给函数,这会消耗较多内存和CPU时间,特别是当结构体很大时。
  2. 传指针:只传递结构体的地址给函数,这非常高效,函数内部可以通过指针修改原始结构体的内容。

示例:

#include <stdio.h>
typedef struct {
    int x;
    int y;
} Point;
// 传值:函数内部修改不会影响原始变量
void printPointByValue(Point p) {
    p.x = 100; // 修改副本
    printf("函数内部 (传值): x = %d, y = %d\n", p.x, p.y);
}
// 传指针:函数内部修改会影响原始变量
void printPointByPointer(Point *p) {
    p->x = 200; // 修改原始变量
    printf("函数内部 (传指针): x = %d, y = %d\n", p->x, p->y);
}
int main() {
    Point myPoint = {10, 20};
    printf("调用前: x = %d, y = %d\n", myPoint.x, myPoint.y);
    printPointByValue(myPoint);
    printf("传值调用后: x = %d, y = %d\n", myPoint.x, myPoint.y); // myPoint.x 仍然是 10
    printPointByPointer(&myPoint);
    printf("传指针调用后: x = %d, y = %d\n", myPoint.x, myPoint.y); // myPoint.x 变成了 200
    return 0;
}

为了避免不必要的拷贝开销,并且允许函数修改原始数据,通常推荐使用指针来传递结构体。


结构体的内存对齐

这是一个高级但非常重要的概念,为了提高内存访问效率,编译器会在结构体成员之间插入一些“填充字节”(Padding),使得每个成员的内存地址都是其自身大小的整数倍。

示例:

#include <stdio.h>
typedef struct {
    char c;   // 1 字节
    int i;    // 4 字节
    short s;  // 2 字节
} TestStruct;
int main() {
    printf("sizeof(char) = %zu\n", sizeof(char));      // 1
    printf("sizeof(int) = %zu\n", sizeof(int));        // 4
    printf("sizeof(short) = %zu\n", sizeof(short));    // 2
    printf("-----------------\n");
    printf("sizeof(TestStruct) = %zu\n", sizeof(TestStruct)); // 输出可能是 12,而不是 1+4+2=7
    return 0;
}

为什么是 12? 内存布局可能是这样的:

[ c ] [ Pad3 ] [ i ] [ s ] [ Pad2 ]
  1      3       4      2      2
  • char c 占 1 字节。
  • 为了让 int i 的地址是 4 的倍数,需要在 c 后面填充 3 个字节。
  • int i 占 4 字节。
  • short s 占 2 字节,其地址已经是 2 的倍数(8),所以不需要填充。
  • 结构体的总大小(12)必须是其中最大成员大小(4)的倍数,因为 7 不是 4 的倍数,所以需要在最后再填充 2 个字节。

如何控制内存对齐? 可以使用 #pragma pack 指令来告诉编译器使用特定的对齐方式,但这通常只在需要与硬件或其他语言交互时才使用。


特性 描述 示例
定义 struct 关键字创建自定义数据类型。 struct Point { int x; int y; };
声明 创建结构体类型的变量。 struct Point p1;typedef struct { ... } Point; Point p1;
成员访问 使用 运算符访问变量成员,-> 访问指针成员。 p1.x = 10; Point *ptr = &p1; ptr->y = 20;
初始化 使用花括号 在声明时初始化。 Point p = {10, 20};
数组 可以创建结构体数组。 Point points[10];
函数参数 可以传值(副本)或传指针(地址),推荐传指针以提高效率。 void func(Point p); vs void func(Point *p);
内存对齐 编译器为优化访问速度而插入填充字节,导致 sizeof 结果可能不等于成员大小之和。 sizeof(MyStruct)

掌握结构体是学习 C 语言迈向高级编程的关键一步,它是理解面向对象编程(C++中的 class 就是在 struct 基础上发展而来的)和复杂数据结构的基础。

-- 展开阅读全文 --
头像
dede arclist如何调用原图?
« 上一篇 2025-12-18
HTML5高端网站建设织梦模板如何提升网站品质?
下一篇 » 2025-12-18
取消
微信二维码
支付宝二维码

目录[+]