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