结构体C语言1006是什么内容?

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

C语言结构体详解

什么是结构体?

在C语言中,我们有了基本的数据类型(如 int, char, float)和数组(存储相同类型的数据),但现实世界中的很多实体,是由多种不同类型的数据组合而成的,一个“学生”信息,可能包含学号(int)、姓名(char数组)、年龄(int)、成绩(float)等。

结构体C语言1006结构体C语言
(图片来源网络,侵删)

为了能够将这些不同类型的数据组合成一个有机的整体,C语言提供了 结构体(Struct) 这种自定义的数据类型。

一句话总结:结构体是一个可以包含不同类型数据成员的集合。


如何定义和声明结构体?

结构体的使用主要分为两步:定义结构体类型声明结构体变量

定义结构体类型

定义结构体类型只是创建了一个“模板”或“蓝图”,它本身不占用内存空间,语法格式如下:

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

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

// 关键字 struct + 结构体名 Student + {成员列表} + 分号
struct Student {
    int id;         // 学号
    char name[50];  // 姓名
    int age;        // 年龄
    float score;    // 成绩
};

注意: 定义结构体类型时,最后的分号 绝对不能少!这是初学者最容易犯的错误之一。

声明结构体变量

定义好结构体类型后,我们就可以像使用 int, char 一样来声明该类型的变量了,系统才会为变量分配内存空间。

先定义类型,后声明变量(推荐)

结构体C语言1006结构体C语言
(图片来源网络,侵删)
struct Student {
    int id;
    char name[50];
    int age;
    float score;
};
// 声明两个 struct Student 类型的变量 s1 和 s2
struct Student s1, s2;

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

// 定义 struct Student 类型,并直接声明变量 s3
struct Student {
    int id;
    char name[50];
    int age;
    float score;
} s3, s4;
// struct Student 类型已定义,s3 和 s4 是变量
// 你还可以继续声明其他变量
struct Student s5;

使用 typedef 关键字(最常用、最方便)

typedef 可以为一个已有的数据类型创建一个新的名字(别名),使用它可以使结构体变量的声明更简洁。

// 定义 struct Student 类型,并为其创建一个别名 "STU"
typedef struct {
    int id;
    char name[50];
    int age;
    float score;
} STU;
// 现在可以直接使用 STU 来声明变量,无需再写 struct
STU s1, s2, s3; 

这种方式代码更简洁,可读性也更好,在实际开发中被广泛使用。


如何访问结构体成员?

我们使用 成员访问运算符() 来访问结构体变量中的具体成员。

语法:结构体变量名.成员名

示例:

#include <stdio.h>
#include <string.h>
// 使用 typedef 定义结构体
typedef struct {
    int id;
    char name[50];
    int age;
    float score;
} Student;
int main() {
    // 声明一个结构体变量
    Student s1;
    // 为成员赋值
    s1.id = 1001;
    s1.age = 18;
    s1.score = 95.5f;
    // 字符串不能直接用 = 赋值,需要使用 strcpy
    strcpy(s1.name, "张三");
    // 打印成员的值
    printf("学号: %d\n", s1.id);
    printf("姓名: %s\n", s1.name);
    printf("年龄: %d\n", s1.age);
    printf("成绩: %.2f\n", s1.score);
    return 0;
}

结构体变量的初始化

在声明变量的同时,可以为其赋初值。

示例:

#include <stdio.h>
#include <string.h>
typedef struct {
    int id;
    char name[50];
    int age;
    float score;
} Student;
int main() {
    // 初始化方式一:按成员顺序
    Student s1 = {1002, "李四", 19, 88.0f};
    // 初始化方式二:指定成员名(C99标准支持,更清晰)
    Student s2 = {
        .id = 1003,
        .name = "王五",
        .age = 20,
        .score = 92.5f
    };
    printf("s1的信息: 学号=%d, 姓名=%s, 年龄=%d, 成绩=%.2f\n", 
           s1.id, s1.name, s1.age, s1.score);
    printf("s2的信息: 学号=%d, 姓名=%s, 年龄=%d, 成绩=%.2f\n", 
           s2.id, s2.name, s2.age, s2.score);
    return 0;
}

结构体数组

如果我们要存储多个学生的信息,可以使用结构体数组。

示例:

#include <stdio.h>
#include <string.h>
typedef struct {
    int id;
    char name[50];
    float score;
} Student;
int main() {
    // 定义一个包含3个元素的 Student 结构体数组
    Student students[3] = {
        {1001, "张三", 90.5},
        {1002, "李四", 85.0},
        {1003, "王五", 92.5}
    };
    // 遍历数组并打印每个学生的信息
    for (int i = 0; i < 3; i++) {
        printf("学生 %d: ID=%d, 姓名=%s, 成绩=%.2f\n", 
               i + 1, students[i].id, students[i].name, students[i].score);
    }
    return 0;
}

结构体指针

指向结构体变量的指针,称为结构体指针,它通常用于在函数间传递大型结构体数据,以提高效率(避免整个结构体的拷贝)。

关键点:

  1. 定义指针:Student *ptr;
  2. 指向变量:ptr = &s1;
  3. 通过指针访问成员:
    • (*ptr).id (先解引用,再用 . 访问成员)
    • ptr->id (使用 -> 运算符,这是更推荐、更简洁的方式)

-> 称为“指向成员运算符”或“箭头运算符”。

示例:

#include <stdio.h>
#include <string.h>
typedef struct {
    int id;
    char name[50];
    float score;
} Student;
int main() {
    Student s1 = {1001, "赵六", 96.0f};
    Student *ptr; // 声明一个结构体指针
    ptr = &s1; // 让指针 ptr 指向 s1 的地址
    // 使用 . 运算符访问
    printf("使用 . 访问: ID=%d, 姓名=%s\n", s1.id, s1.name);
    // 使用 -> 运算符访问
    printf("使用 -> 访问: ID=%d, 姓名=%s\n", ptr->id, ptr->name);
    // 也可以使用 (*ptr).id 的形式
    printf("使用 (*ptr). 访问: ID=%d\n", (*ptr).id);
    return 0;
}

结构体作为函数参数

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

  1. 值传递: 将结构体变量的副本传递给函数,函数内部修改不会影响原始变量。
  2. 地址传递(指针传递): 将结构体变量的地址传递给函数,函数可以通过指针修改原始变量,且效率更高(特别是结构体很大时)。

示例:

#include <stdio.h>
typedef struct {
    int x;
    int y;
} Point;
// 值传递:修改的是副本,不影响主函数中的 p1
void modifyByValue(Point p) {
    p.x = 100;
    p.y = 200;
    printf("函数内部 (值传递): x=%d, y=%d\n", p.x, p.y);
}
// 地址传递:修改的是原始变量
void modifyByPointer(Point *p) {
    p->x = 300;
    p->y = 400;
    printf("函数内部 (指针传递): x=%d, y=%d\n", p->x, p->y);
}
int main() {
    Point p1 = {10, 20};
    printf("调用函数前: x=%d, y=%d\n", p1.x, p1.y);
    modifyByValue(p1);
    printf("值传递后:   x=%d, y=%d\n", p1.x, p1.y); // 值未改变
    modifyByPointer(&p1);
    printf("指针传递后:  x=%d, y=%d\n", p1.x, p1.y); // 值已改变
    return 0;
}

结构体嵌套

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

示例:

#include <stdio.h>
#include <string.h>
// 定义日期结构体
typedef struct {
    int year;
    int month;
    int day;
} Date;
// 定义学生结构体,其中包含一个 Date 类型的成员
typedef struct {
    int id;
    char name[50];
    Date birthday; // 嵌套结构体
} Student;
int main() {
    Student s1;
    s1.id = 1001;
    strcpy(s1.name, "孙七");
    // 访问嵌套结构体的成员
    s1.birthday.year = 2005;
    s1.birthday.month = 8;
    s1.birthday.day = 10;
    printf("学生ID: %d\n", s1.id);
    printf("学生姓名: %s\n", s1.name);
    printf("出生日期: %d-%02d-%02d\n", 
           s1.birthday.year, s1.birthday.month, s1.birthday.day);
    return 0;
}

结构体的大小与内存对齐

在C语言中,结构体的大小并不等于其所有成员大小之和,为了提高CPU的访问效率,编译器会进行内存对齐

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

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

示例:

#include <stdio.h>
typedef struct {
    char c1;    // 1字节
    int i;      // 4字节
    char c2;    // 1字节
} Test;
int main() {
    printf("sizeof(Test) = %zu\n", sizeof(Test)); // 输出可能是 12
    // 分析:
    // c1 放在偏移量 0,占 1 字节。
    // i 需要对齐到 4 字节边界,所以从偏移量 4 开始,占 4 字节。
    // c2 放在偏移量 8,占 1 字节。
    // 总大小是 9,但需要是最大对齐数 (4) 的倍数,所以补 3 个字节,最终大小为 12。
    return 0;
}

如何修改结构体以节省空间? 将小的成员放在一起,大的成员放在一起,可以减少内存填充,节省空间。

typedef struct {
    char c1;    // 1字节
    char c2;    // 1字节
    int i;      // 4字节
} Test2;
printf("sizeof(Test2) = %zu\n", sizeof(Test2)); // 输出 8

结构体与 const

当使用结构体指针作为函数参数,并且不希望函数内部修改结构体的内容时,可以使用 const 关键字来修饰指针。

void printStudentInfo(const Student *s) {
    // s->id = 999; // 错误!不能修改 const 指针指向的内容
    printf("ID: %d\n", s->id); // 读取是允许的
}

这既保证了数据的安全性,又避免了值传递带来的性能开销。


特性 描述 示例
定义 自定义数据类型,组合不同类型成员。 struct Student { ... };
声明变量 使用定义好的类型创建变量。 struct Student s1;STU s1; (使用typedef)
访问成员 使用 运算符。 s1.id = 100;
指针访问 使用 -> 运算符。 ptr->id = 100;
初始化 声明时赋初值。 Student s = {1001, "Tom"};
数组 存储多个结构体。 Student class[30];
函数参数 值传递(安全,低效)或指针传递(可修改,高效)。 void func(Student s)void func(Student *p)
嵌套 结构体成员可以是另一个结构体。 Date birthday;
内存对齐 编译器为提高效率进行的内存填充。 sizeof 结果可能大于成员之和。

掌握结构体是学习C语言面向对象思想(C++中的类)和复杂数据结构(如链表、树)的基础,希望这份详细的教程能对你有所帮助!

-- 展开阅读全文 --
头像
C语言使用现状如何,是否已过时?
« 上一篇 2025-12-20
dede如何提取文章第一张图为缩略图?
下一篇 » 2025-12-20

相关文章

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

目录[+]