struction c语言

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

struct 是 C 语言中一种非常重要的复合数据类型,你可以把它想象成一个自定义的“蓝图”或“模板”,它允许你将不同类型的数据项组合成一个单一的类型。

struction c语言
(图片来源网络,侵删)

为什么需要 struct

想象一下你要描述一个“学生”的信息,一个学生有:

  • 姓名(字符串)
  • 学号(整数)
  • 年龄(整数)
  • 平均成绩(浮点数)

在 C 语言中,如果使用独立的变量来存储这些信息,会非常混乱且难以管理:

char name[50];
int id;
int age;
float score;

当你需要传递一个学生的所有信息时,你必须一个一个地传递这些变量,而 struct 可以完美地解决这个问题。


struct 的基本定义和使用

1 定义结构体类型

使用 struct 关键字来定义一个新的数据类型,语法如下:

struction c语言
(图片来源网络,侵删)
struct 结构体名称 {
    数据类型 成员1;
    数据类型 成员2;
    // ...
};

示例:定义一个 Student 结构体

// 定义一个名为 Student 的结构体类型
struct Student {
    char name[50];
    int id;
    int age;
    float score;
};

重要提示

  • 这只是定义了一个类型,就像 int, float 一样,它还没有分配内存空间,它只是一个模板。
  • 定义结束时,需要一个分号 ,这是初学者最容易忘记的错误之一。

2 声明结构体变量

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

直接声明

struction c语言
(图片来源网络,侵删)
struct Student student1; // 声明一个名为 student1 的 Student 类型的变量

编译器会为 student1 分配一块连续的内存空间,足以容纳 name, id, age, 和 score

在定义时声明

struct Student {
    char name[50];
    int id;
    int age;
    float score;
} student1, student2; // 同时声明了两个变量 student1 和 student2

使用 typedef(推荐方式) 为了简化代码,通常使用 typedef 为结构体类型创建一个简短的别名。

typedef struct {
    char name[50];
    int id;
    int age;
    float score;
} Student; // Student 现在是一个类型别名,可以直接使用
// 现在声明变量就非常简洁了
Student student1;
Student student2;

这是 C 语言中非常普遍和推荐的做法。

3 访问结构体成员

使用成员访问运算符( 点运算符)来访问和修改结构体内部的数据。

#include <stdio.h>
#include <string.h>
// 使用 typedef 定义 Student 类型
typedef struct {
    char name[50];
    int id;
    int age;
    float score;
} Student;
int main() {
    // 声明并初始化一个结构体变量
    Student student1;
    student1.id = 1001;
    student1.age = 20;
    student1.score = 95.5;
    strcpy(student1.name, "张三"); // 使用 strcpy 复制字符串到字符数组
    // 声明并初始化另一个变量
    Student student2 = {"李四", 1002, 21, 88.0};
    // 打印结构体成员信息
    printf("学生1的信息:\n");
    printf("  姓名: %s\n", student1.name);
    printf("  学号: %d\n", student1.id);
    printf("  年龄: %d\n", student1.age);
    printf("  成绩: %.1f\n", student1.score);
    printf("\n学生2的信息:\n");
    printf("  姓名: %s\n", student2.name);
    printf("  学号: %d\n", student2.id);
    printf("  年龄: %d\n", student2.age);
    printf("  成绩: %.1f\n", student2.score);
    return 0;
}

结构体的初始化

可以在声明结构体变量的同时,使用花括号 进行初始化,初始化的顺序必须与结构体定义中成员的顺序一致。

Student student = {"王五", 1003, 22, 91.2};

如果只想初始化部分成员,C99 标准支持指定初始化器:

Student student = {.id = 1004, .score = 76.5}; // 其他成员会被自动初始化为 0 或空字符串

结构体数组

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

#include <stdio.h>
typedef struct {
    char name[20];
    int id;
} Person;
int main() {
    // 定义一个 Person 结构体数组,包含 3 个元素
    Person people[3] = {
        {"Alice", 1},
        {"Bob", 2},
        {"Charlie", 3}
    };
    // 遍历并打印数组内容
    for (int i = 0; i < 3; i++) {
        printf("姓名: %s, ID: %d\n", people[i].name, people[i].id);
    }
    return 0;
}

结构体指针

和普通变量一样,你也可以声明指向结构体的指针。

1 声明和初始化

Student student = {"David", 1005, 23, 89.0};
Student *ptr = &student; // ptr 是一个指向 student 变量的指针

2 通过指针访问成员

有两种方式:

  1. *使用 `解引用,然后使用.`**

    printf("姓名: %s\n", (*ptr).name);
  2. 使用 ->(箭头运算符)(更常用、更简洁) -> 是 的缩写,专门用于通过指针访问结构体成员。

    printf("姓名: %s\n", ptr->name);
    printf("学号: %d\n", ptr->id);
    printf("年龄: %d\n", ptr->age);
    printf("成绩: %.1f\n", ptr->score);

结构体作为函数参数

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

1 传值(Pass-by-Value)

将结构体的副本传递给函数,函数内部对结构体的修改不会影响到原始结构体,当结构体很大时,这种方式效率较低,因为需要复制大量数据。

#include <stdio.h>
typedef struct {
    int x;
    int y;
} Point;
// 函数接收一个 Point 结构体的副本
void printPoint(Point p) {
    printf("Point: (%d, %d)\n", p.x, p.y);
}
// 函数修改副本,不影响原始变量
void movePoint(Point p) {
    p.x += 10;
    p.y += 10;
    printf("Inside movePoint: (%d, %d)\n", p.x, p.y);
}
int main() {
    Point myPoint = {5, 5};
    printPoint(myPoint);
    movePoint(myPoint); // 传递副本
    printf("After movePoint: (%d, %d)\n", myPoint.x, myPoint.y); // myPoint 保持不变
    return 0;
}

2 传指针(Pass-by-Pointer/Reference)

将结构体的地址传递给函数,函数可以通过指针直接修改原始结构体,这种方式效率更高,因为它只传递一个地址(通常是 4 或 8 字节),无论结构体多大。

#include <stdio.h>
typedef struct {
    int x;
    int y;
} Point;
// 函数接收一个 Point 结构体的指针
void printPointPtr(const Point *p) { // 使用 const 表示不通过指针修改数据
    printf("Point: (%d, %d)\n", p->x, p->y);
}
// 函数通过指针修改原始结构体
void movePointPtr(Point *p) {
    p->x += 10;
    p->y += 10;
}
int main() {
    Point myPoint = {5, 5};
    printPointPtr(&myPoint);
    movePointPtr(&myPoint); // 传递地址
    printf("After movePointPtr: (%d, %d)\n", myPoint.x, myPoint.y); // myPoint 被修改了
    return 0;
}

最佳实践:为了避免不必要的拷贝并允许函数修改原始数据,通常推荐传递结构体指针,如果函数只需要读取结构体内容而不修改,可以在指针参数前加上 const 关键字,这是一个非常好的编程习惯。


结构体的大小与内存对齐

结构体的大小不一定是其所有成员大小之和,这涉及到内存对齐问题。

为什么需要内存对齐?

  1. 平台原因:不是所有的硬件平台都能在任何地址上访问任意类型的数据,某些硬件平台只能在特定地址(如偶数地址)访问特定类型的数据。
  2. 性能原因:经过内存对齐的数据,CPU 可以一次性读取一个完整的数据,而不需要多次读取和拼接,从而提高访问效率。

规则(简化版):

  1. 结构体的第一个成员 offset 为 0。
  2. 其他成员的 offset 是其自身大小的整数倍。
  3. 结构体的总大小必须是最大成员大小的整数倍。

示例:

#include <stdio.h>
struct A {
    char c;    // 1 字节
    int i;     // 4 字节
}; // 大小应该是 8 字节 (1 + 填充3 + 4)
struct B {
    int i;     // 4 字节
    char c;    // 1 字节
    short s;   // 2 字节
}; // 大小应该是 12 字节 (4 + 1 + 填充1 + 2)
struct C {
    char c1;   // 1
    char c2;   // 1
    int i;     // 4
}; // 大小应该是 8 字节 (1 + 1 + 填充2 + 4)
int main() {
    printf("sizeof(struct A): %zu\n", sizeof(struct A)); // 输出 8
    printf("sizeof(struct B): %zu\n", sizeof(struct B)); // 输出 12
    printf("sizeof(struct C): %zu\n", sizeof(struct C)); // 输出 8
    return 0;
}

如何优化结构体大小? 将小的数据类型放在一起,大的数据类型放在一起,可以减少填充字节,从而节省内存。struct C 就比 struct A 更紧凑。


特性 描述 示例
定义 创建一个自定义的复合数据类型模板。 struct Student {...};
声明变量 使用定义的类型创建变量。 Student s1;
成员访问 使用 运算符访问变量成员。 s1.name = "Tom";
指针访问 使用 -> 运算符通过指针访问成员。 ptr->age = 20;
函数参数 推荐使用指针传参以提高效率,const 用于只读场景。 void func(const Student *s);
内存布局 需要考虑内存对齐,会影响结构体的大小。 sizeof(struct)

struct 是 C 语言进行数据组织和建模的基石,掌握它是从 C 语言初级迈向中级的必经之路,它使得代码更加清晰、模块化,并且更贴近现实世界的问题。

-- 展开阅读全文 --
头像
dede选项卡如何实现对应链接跳转?
« 上一篇 03-01
dede管理员文档转移
下一篇 » 03-01

相关文章

取消
微信二维码
支付宝二维码

目录[+]