首页 > 编程笔记 > C语言笔记(精华版)

C语言结构体用法详解(包括定义、初始化、赋值、成员访问等细节)

在现实生活中,我们经常需要用多个不同类型的数据来描述一个对象,比如要描述一个学生,需要记录他的姓名、学号、年龄、成绩等信息;要描述一个商品,需要记录它的名称、价格、库存等信息。


如果使用普通的变量来存储这些数据,不仅需要定义很多独立的变量,而且这些变量之间缺乏关联性,不利于数据的管理和使用。为了解决这个问题,C语言提供了结构体(Structure)这种复合数据类型。
 

结构体可以将多个不同类型的数据组织在一起,形成一个新的数据类型。这些数据可以是基本数据类型(如 int、float、char 等),也可以是其他的结构体类型。每个数据在结构体中被称为成员或者字段。通过结构体,我们可以将所有相关的数据打包成一个整体,更好地表达数据之间的逻辑关系。

结构体的定义

要使用结构体,需要先定义结构体类型,它的基本语法格式如下:

struct 结构体名称 {
    成员类型1 成员名称1;
    成员类型2 成员名称2;
    ...
    成员类型n 成员名称n;
};

struct 是定义结构体的关键字,结构体名称用于标识这个新的数据类型,花括号内部包含了若干成员变量的声明。每个成员变量都有自己的类型和名称,它们之间用分号分隔。
 

下面通过一个具体的例子来说明结构体的定义和使用。假设我们要开发一个学生信息管理系统,需要存储学生的基本信息:

struct Student {
    char name[50];    // 姓名
    int id;          // 学号
    int age;         // 年龄
    float score;     // 成绩
};

这里定义了一个名为 Student 的结构体类型,它包含 4 个成员。注意,为了和变量名、函数名进行区分,结构体名的首字母一般要大写。

结构体的使用

结构体是一种类型,而不是变量,不能赋值;定义了结构体类型后,还需要声明结构体变量才能使用。

声明结构体变量

声明结构体变量有两种方式:先定义类型再声明变量,或者在定义类型的同时声明变量。

// 方式 1:先定义类型,再声明变量
struct Student s1;  //使用了已经定义的结构体类型 Student

// 方式 2:定义类型的同时声明变量
struct Student {
    char name[50];
    int id;
    int age;
    float score;
} s1, s2;

初始化结构体成员

结构体变量的成员可以在声明时初始化,也可以在声明后单独赋值。初始化时要注意成员的顺序必须与定义时的顺序一致。

// 声明时初始化
struct Student s1 = {"张三", 10001, 18, 85.5};

// 声明后单独赋值
struct Student s2;
strcpy(s2.name, "李四");    // 字符数组不能直接赋值,需要使用 strcpy
s2.id = 10002;
s2.age = 19;
s2.score = 92.5;

访问结构体成员

如果是结构体变量,可以使用点运算符.访问结构体成员;如果是结构体指针,则使用箭头运算符->访问结构体成员。请看下面的例子:

#include <stdio.h>
#include <string.h>

struct Student {
    char name[50];
    int id;
    int age;
    float score;
};

int main() {
    struct Student s1 = {"张三", 10001, 18, 85.5};
    struct Student *ptr = &s1;
    
    // 使用点运算符访问成员
    printf("姓名:%s\n", s1.name);
    printf("学号:%d\n", s1.id);
    
    // 使用箭头运算符访问成员
    printf("年龄:%d\n", ptr->age);
    printf("成绩:%.1f\n", ptr->score);
    
    return 0;
}

输出结果:

姓名:张三
学号:10001
年龄:18
成绩:85.5

结构体变量之间的赋值

结构体变量之间可以直接赋值,这将复制所有成员的值,也即把一个结构体的内存直接拷贝给另一个结构体。
 

但是需要注意,这是一种浅拷贝,如果结构体中包含指针成员,赋值操作只会复制指针的值,而不会复制指针指向的数据,这种情况下需要手动进行深拷贝。


请看下面的例子:

#include <stdio.h>
#include <string.h>

struct Student {
    char name[50];
    int age;
    float score;
};

int main() {
    struct Student stu1 = {"张三", 18, 92.5};
    struct Student stu2;
    
    // 结构体变量赋值
    stu2 = stu1;
    
    // 修改 stu2 的成员
    strcpy(stu2.name, "李四");
    stu2.age = 20;
    
    printf("stu1:%s, %d, %.1f\n", stu1.name, stu1.age, stu1.score);
    printf("stu2:%s, %d, %.1f\n", stu2.name, stu2.age, stu2.score);
    
    return 0;
}

输出结果:

stu1:张三, 18, 92.5
stu2:李四, 20, 92.5

结构体的嵌套定义

结构体还可以嵌套定义,即一个结构体的成员可以是另一个结构体,这种特性使得我们可以构建更复杂的数据类型。请看下面的代码:

struct Date {
    int year;
    int month;
    int day;
};

struct Student {
    char name[50];
    int age;
    struct Date birthday;    // 结构体嵌套
    float score;
};

// 嵌套结构体的初始化
struct Student stu = {
    "张三",
    18,
    {2005, 8, 15},    // 嵌套的结构体成员也用花括号
    92.5
};

使用 typedef 重命名

在实际编程中,为了简化代码,我们经常使用 typedef 关键字为结构体类型定义一个新的类型名,这样在声明变量时就可以省略 struct 关键字。请看代码:

typedef struct Student {
    char name[50];
    int age;
    float score;
} Stu;    // 定义别名 Stu

// 使用别名定义变量,不需要再写 struct
Stu stu1 = {"张三", 18, 92.5};
Stu stu2;

结构体类型的大小

需要注意的是,结构体的大小不一定等于所有成员大小的简单相加。由于内存对齐的原因,编译器可能会在成员之间插入一些填充字节。如果需要了解结构体的实际大小,可以使用 sizeof 运算符:

#include <stdio.h>

struct Test {
    char c;     // 1 字节
    int i;      // 4 字节
    double d;   // 8 字节
};

int main() {
    printf("结构体大小:%zu 字节\n", sizeof(struct Test));
    return 0;
}

输出结果可能是:

结构体大小:16 字节

虽然成员大小加起来是 13 字节,但由于内存对齐,实际的结构体大小是 16 字节。这种内存对齐机制是为了提高程序的运行效率,但同时也会占用更多的内存空间。

 

总起来说,结构体是一种自定义的、复合的数据类型,而不是变量。变量可以直接使用,而数据类型还需要声明变量才能使用。理解这一点非常重要,这是结构体的本质。

结构体在C语言编程中有着广泛的应用,通过合理使用结构体,我们可以更好地组织和管理复杂的数据。在开发较复杂的程序时,结构体常常与其他高级特性(如动态内存分配、文件操作等)结合使用,构建更复杂的数据结构和算法。

相关文章