结构体是一种自定义的数据类型,它把不同类型的数据组合成一个整体,方便管理和操作相关的数据。在定义结构体时,使用 struct 关键字,后面跟着结构体的名称,再用花括号 {} 包含结构体的成员列表,每个成员由数据类型和成员名组成,成员之间用分号 ; 分隔。
struct 结构体名 {
数据类型 成员1;
数据类型 成员2;
// 可以有更多成员
};
例如,定义一个描述图书信息的结构体:
struct Book {
char title[100]; // 书名,用字符数组存储
char author[50]; // 作者,用字符数组存储
int year; // 出版年份,用整数存储
float price; // 价格,用浮点数存储
};
注意事项和细节
先定义结构体,再声明变量:这是最常见的方式,先定义好结构体类型,然后在需要使用的地方声明该类型的变量。
struct Book {
char title[100];
char author[50];
int year;
float price;
};
struct Book book1; // 声明一个 Book 类型的变量 book1
在定义结构体的同时声明变量:这种方式在定义结构体的同时就声明了变量,适用于只在当前代码块使用该结构体类型的情况。
struct Movie {
char name[80];
int duration;
} movie1, movie2; // 定义 Movie 结构体的同时声明了两个变量 movie1 和 movie2
使用匿名结构体声明变量:匿名结构体没有结构体名,只能在定义时声明变量,之后无法再声明该类型的其他变量,使用场景较少。
struct {
int x;
int y;
} point; // 声明一个匿名结构体类型的变量 point
按成员顺序初始化:按照结构体定义中成员的顺序,依次为每个成员赋值。
struct Book book2 = {"C Programming", "John Doe", 2020, 29.99};
指定成员名初始化(C99 及以后支持):可以不按照成员的顺序,通过指定成员名来初始化,提高代码的可读性和可维护性。
struct Book book3 = {.author = "Jane Smith", .title = "Data Structures", .year = 2021, .price = 39.99};
注意事项和细节
struct Book book4 = {"Python Basics"}; // 只初始化了 title 成员,其他成员自动初始化为 0 或空字符
使用点运算符 . 来访问结构体变量的成员。点运算符的左边是结构体变量名,右边是结构体的成员名。
#include
#include
struct Book {
char title[100];
char author[50];
int year;
float price;
};
int main() {
struct Book book;
strcpy(book.title, "Java Programming"); // 给 title 成员赋值
strcpy(book.author, "Mark Johnson"); // 给 author 成员赋值
book.year = 2019; // 给 year 成员赋值
book.price = 49.99; // 给 price 成员赋值
printf("Title: %s\n", book.title);
printf("Author: %s\n", book.author);
printf("Year: %d\n", book.year);
printf("Price: %.2f\n", book.price);
return 0;
}
注意事项和细节
结构体数组是由多个相同结构体类型的元素组成的数组。可以将多个相关的结构体变量组织在一起,方便进行批量处理。
#include
struct Book {
char title[100];
char author[50];
int year;
float price;
};
int main() {
struct Book books[3] = {
{"C++ Primer", "Stanley Lippman", 2012, 79.99},
{"Effective Java", "Joshua Bloch", 2018, 59.99},
{"The Pragmatic Programmer", "Andrew Hunt", 2019, 49.99}
};
for (int i = 0; i < 3; i++) {
printf("Book %d:\n", i + 1);
printf("Title: %s\n", books[i].title);
printf("Author: %s\n", books[i].author);
printf("Year: %d\n", books[i].year);
printf("Price: %.2f\n", books[i].price);
printf("\n");
}
return 0;
}
注意事项和细节
可以定义指向结构体的指针,通过指针来访问结构体的成员。使用 -> 运算符来访问指针所指向结构体的成员,-> 运算符是 (*指针).成员 的简写形式。
#include
#include
struct Book {
char title[100];
char author[50];
int year;
float price;
};
int main() {
struct Book book = {"JavaScript: The Definitive Guide", "David Flanagan", 2020, 69.99};
struct Book *p = &book; // 定义一个指向 Book 结构体的指针 p,并指向 book 变量
printf("Title: %s\n", p->title);
printf("Author: %s\n", p->author);
printf("Year: %d\n", p->year);
printf("Price: %.2f\n", p->price);
return 0;
}
注意事项和细节
#include
#include
#include
struct Book {
char title[100];
char author[50];
int year;
float price;
};
int main() {
struct Book *p = (struct Book *)malloc(sizeof(struct Book));
if (p == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
strcpy(p->title, "Python Crash Course");
strcpy(p->author, "Eric Matthes");
p->year = 2015;
p->price = 39.99;
printf("Title: %s\n", p->title);
printf("Author: %s\n", p->author);
printf("Year: %d\n", p->year);
printf("Price: %.2f\n", p->price);
free(p); // 释放动态分配的内存
return 0;
}
结构体可以嵌套,即一个结构体的成员可以是另一个结构体类型。通过结构体嵌套,可以更复杂地组织数据。
#include
struct Date {
int year;
int month;
int day;
};
struct Book {
char title[100];
char author[50];
struct Date publishDate; // 嵌套 Date 结构体
float price;
};
int main() {
struct Book book = {
"The C Programming Language",
"Brian Kernighan",
{1978, 2, 22}, // 初始化嵌套的 Date 结构体
29.99
};
printf("Title: %s\n", book.title);
printf("Author: %s\n", book.author);
printf("Publish Date: %d-%d-%d\n", book.publishDate.year, book.publishDate.month, book.publishDate.day);
printf("Price: %.2f\n", book.price);
return 0;
}
注意事项和细节
将结构体变量的值复制一份传递给函数,函数内部对参数的修改不会影响原结构体变量。
#include
struct Point {
int x;
int y;
};
// 函数接受一个 Point 结构体作为参数
void printPoint(struct Point p) {
printf("Point: (%d, %d)\n", p.x, p.y);
}
int main() {
struct Point p = {3, 4};
printPoint(p); // 传递结构体变量 p 的值给函数
return 0;
}
注意事项和细节
将结构体变量的地址传递给函数,函数内部可以通过指针修改原结构体变量的值。
#include
struct Point {
int x;
int y;
};
// 函数接受一个指向 Point 结构体的指针作为参数
void movePoint(struct Point *p, int dx, int dy) {
p->x += dx;
p->y += dy;
}
int main() {
struct Point p = {3, 4};
movePoint(&p, 1, 2); // 传递结构体变量 p 的地址给函数
printf("New Point: (%d, %d)\n", p.x, p.y);
return 0;
}
注意事项和细节
结构体的成员在内存中并不是连续存储的,编译器会根据成员的类型和平台的要求进行内存对齐,以提高访问效率。内存对齐的规则如下:
#include
struct Example {
char c; // 1 字节
int i; // 4 字节
char d; // 1 字节
};
int main() {
printf("Size of struct Example: %zu\n", sizeof(struct Example));
return 0;
}
在这个例子中,由于内存对齐的原因,struct Example 的大小可能不是 6 字节(1 + 4 + 1),而是 12 字节。char c 从偏移量 0 开始存储,占 1 个字节;int i 由于偏移量必须是 4 的整数倍,所以从偏移量 4 开始存储,占 4 个字节;char d 从偏移量 8 开始存储,占 1 个字节;为了满足结构体总大小是最大成员类型大小(4 字节)的整数倍,结构体的总大小为 12 字节。
注意事项和细节
位域允许在一个结构体中以位为单位来指定成员所占的存储空间,用于节省内存。
#include
struct Flags {
unsigned int flag1 : 1; // 占 1 位
unsigned int flag2 : 1; // 占 1 位
unsigned int flag3 : 1; // 占 1 位
unsigned int flag4 : 1; // 占 1 位
};
int main() {
struct Flags f;
f.flag1 = 1;
f.flag2 = 0;
f.flag3 = 1;
f.flag4 = 0;
printf("Flag1: %d\n", f.flag1);
printf("Flag2: %d\n", f.flag2);
printf("Flag3: %d\n", f.flag3);
printf("Flag4: %d\n", f.flag4);
return 0;
}
在这个例子中,struct Flags 的四个成员 flag1、flag2、flag3 和 flag4 各占 1 位,总共只占 1 个字节的存储空间。
注意事项和细节
struct Example {
unsigned int flag1 : 1;
unsigned int : 3; // 未命名位域,占 3 位
unsigned int flag2 : 1;
};