C语言 结构体——《跟老吕学C》
C语言 结构体
一、C语言结构体的基本概念
结构体(struct)是C语言中一种自定义的数据类型,允许我们组合多种数据类型到一个单独的类型名称中。通过结构体,我们可以创建一个数据类型,该类型可以包含多个不同类型的成员,如整型、浮点型、字符型等,甚至其他结构体类型。
二、C语言结构体的定义与初始化
1. 结构体的定义
在C语言中,我们可以使用struct
关键字来定义结构体。以下是一个简单的结构体定义示例:
struct Student {
char name[50];
int age;
float score;
};
在这个例子中,我们定义了一个名为Student
的结构体,它有三个成员:一个字符数组name
用于存储学生的名字,一个整型age
用于存储学生的年龄,和一个浮点型score
用于存储学生的分数。
2. 结构体的初始化
在C语言中,我们可以在定义结构体变量的同时对其进行初始化。以下是一个示例:
struct Student student1 = {"Tom", 20, 90.5};
在这个例子中,我们创建了一个名为student1
的Student
结构体变量,并初始化了其成员。
3. 结构体的初始化(指定成员)
除了上面提到的直接初始化方式,C语言还支持在初始化结构体时指定成员进行赋值。这在某些情况下会更为灵活和清晰。以下是一个示例:
struct Student student2 = {.age = 22, .name = "Jerry", .score = 88.0};
在这个例子中,我们同样创建了一个名为student2
的Student
结构体变量,但这次我们在初始化时指定了成员的名字和对应的值。注意,成员的顺序可以与结构体定义中的顺序不同,只要指定了正确的成员名即可。
4. 结构体数组的初始化
结构体数组允许我们存储多个具有相同类型的数据结构。在初始化结构体数组时,我们可以为每个元素提供初始值,或者仅初始化部分元素。以下是一个示例:
struct Student students[] = {
{"Alice", 19, 92.0},
{"Bob", 21, 85.5},
// 第三个元素未初始化,将包含随机值
};
在这个例子中,我们定义了一个名为students
的结构体数组,并初始化了前两个元素。第三个元素未被显式初始化,因此将包含随机值(在实际编程中,未初始化的变量应始终视为包含随机值,并应在使用前进行初始化)。
5. 结构体指针的初始化
在C语言中,我们还可以使用指针来操作结构体。当我们使用指针时,需要首先分配内存来存储结构体数据,然后才能初始化它。以下是一个示例:
struct Student *studentPtr;
studentPtr = (struct Student *)malloc(sizeof(struct Student));
if (studentPtr != NULL) {
strcpy(studentPtr->name, "Mike");
studentPtr->age = 18;
studentPtr->score = 95.0;
}
// 使用完毕后,记得释放内存
free(studentPtr);
在这个例子中,我们首先定义了一个指向Student
结构体的指针studentPtr
。然后,我们使用malloc
函数为结构体分配内存,并将返回的内存地址赋值给studentPtr
。接着,我们使用指针来访问结构体的成员并进行初始化。最后,在完成对结构体的操作后,我们使用free
函数释放了分配的内存。
6. 注意事项
- 在初始化结构体时,确保提供的值与结构体的成员类型和数量相匹配。
- 使用结构体指针时,务必确保在访问成员之前已经为结构体分配了内存。
- 在使用完通过
malloc
分配的内存后,应使用free
函数释放内存,以避免内存泄漏。 - 在结构体数组中,未初始化的元素将包含随机值,因此在使用前应进行初始化或检查。
三、C语言结构体的使用
1. 访问结构体的成员
我们可以使用.
操作符来访问结构体的成员。以下是一个示例:
#include <stdio.h>
struct Student {
char name[50];
int age;
float score;
};
int main() {
struct Student student1 = {"Tom", 20, 90.5};
printf("Name: %s, Age: %d, Score: %.1f\n", student1.name, student1.age, student1.score);
return 0;
}
在这个例子中,我们创建了一个Student
结构体变量student1
,并使用printf
函数打印了其成员的值。
2. 结构体数组
在C语言中,为了方便处理具有相同类型的多个数据项,我们可以将多个结构体变量组织到一个数组中。以下是一个具体的示例:
#include <stdio.h>
struct Student {
char name[50];
int age;
float score;
};
int main() {
struct Student students[3] = {
{"Tom", 20, 90.5},
{"Jerry", 21, 88.0},
{"Alice", 19, 92.5}
};
// 遍历并打印结构体数组中的每个元素
for (int i = 0; i < 3; i++) {
printf("Name: %s, Age: %d, Score: %.1f\n", students[i].name, students[i].age, students[i].score);
}
return 0;
}
在这个例子中,我们首先定义了一个名为Student
的结构体,它包含了三个成员:name
(用于存储学生姓名)、age
(用于存储学生年龄)和score
(用于存储学生成绩)。然后,在main
函数中,我们创建了一个名为students
的Student
结构体数组,并初始化了三个元素。接着,我们使用一个for
循环遍历这个数组,并使用printf
函数打印出每个结构体变量的成员值。
1. 结构体数组的输入
在实际应用中,我们可能希望从用户或其他输入源动态地获取结构体数组的元素值。下面是一个简单的示例,演示如何从标准输入(键盘)读取students
数组的元素:
#include <stdio.h>
#include <string.h>
// ...(结构体定义保持不变)
int main() {
struct Student students[3];
// 从用户输入获取结构体数组的元素
for (int i = 0; i < 3; i++) {
printf("Enter name for student %d: ", i + 1);
scanf("%49s", students[i].name); // 使用%49s防止缓冲区溢出
printf("Enter age for student %d: ", i + 1);
scanf("%d", &students[i].age);
printf("Enter score for student %d: ", i + 1);
scanf("%f", &students[i].score);
}
// 遍历并打印结构体数组中的每个元素(此部分保持不变)
// ...
return 0;
}
2. 结构体数组的排序
假设我们想要按照学生的成绩(score
成员)对students
数组进行排序。由于C语言标准库没有直接为结构体数组提供排序函数,我们需要自己编写一个比较函数,并使用某种排序算法(如冒泡排序、选择排序、快速排序等)进行排序。
下面是一个使用冒泡排序算法对students
数组按照成绩进行排序的示例:
#include <stdio.h>
#include <string.h>
// ...(结构体定义保持不变)
// 比较函数,用于冒泡排序
int compare(const void *a, const void *b) {
struct Student *studentA = (struct Student *)a;
struct Student *studentB = (struct Student *)b;
return (studentA->score - studentB->score);
}
int main() {
// ...(结构体数组输入部分保持不变)
// 使用qsort函数对结构体数组进行排序
qsort(students, 3, sizeof(struct Student), compare);
// 遍历并打印排序后的结构体数组中的每个元素
for (int i = 0; i < 3; i++) {
printf("Name: %s, Age: %d, Score: %.1f\n", students[i].name, students[i].age, students[i].score);
}
return 0;
}
注意,这里我们使用了C标准库中的qsort
函数进行排序,它需要一个比较函数作为参数。我们定义的compare
函数就是这样一个比较函数,它根据结构体中的score
成员进行比较。
3. 结构体指针传参
在C语言中,我们还可以使用指针来操作结构体。以下是一个示例:
#include <stdio.h>
struct Student {
char name[50];
int age;
float score;
};
// 定义一个函数,用于打印学生信息,该函数接收一个指向Student结构体的指针作为参数
void printStudentInfo(struct Student *student) {
printf("Name: %s, Age: %d, Score: %.1f\n", student->name, student->age, student->score);
}
int main() {
struct Student student1 = {"Tom", 20, 90.5};
struct Student *p = &student1;
// 直接通过结构体指针打印学生信息
printf("Using direct pointer: ");
printf("Name: %s, Age: %d, Score: %.1f\n", p->name, p->age, p->score);
// 使用定义的函数,并通过指针作为参数传递学生信息
printf("Using function with pointer argument: ");
printStudentInfo(p);
return 0;
}
在这个扩展的例子中,我们定义了一个名为printStudentInfo
的函数,该函数接收一个指向Student
结构体的指针作为参数。在函数内部,我们使用->
操作符来访问指针所指向的结构体的成员,并打印相关信息。
在main
函数中,我们首先创建了一个Student
类型的结构体变量student1
,并初始化其成员。然后,我们创建了一个指向该结构体的指针p
,并将其初始化为student1
的地址。
接下来,我们做了两件事情:首先,我们直接通过结构体指针p
打印了student1
的信息;然后,我们调用了printStudentInfo
函数,并将指针p
作为参数传递,以同样的方式打印了student1
的信息。
使用结构体指针的好处之一是可以提高代码的灵活性和可重用性。通过将结构体的地址作为参数传递给函数,我们可以在不改变函数接口的情况下,操作不同的结构体实例。这对于处理具有相同结构但数据内容不同的多个对象非常有用。
4. C语言结构体函数调用
在C语言中,结构体是一种用户自定义的数据类型,它允许我们将不同类型的数据组合成一个单一的类型。然而,需要注意的是,结构体本身并不包含函数成员,也就是说,我们不能直接在结构体中定义函数。但我们可以为结构体创建函数,这些函数通常被称为“结构体函数”或“与结构体相关的函数”。
尽管我们不能在结构体中直接定义函数,但我们可以在结构体外部定义函数,并在这些函数中传递结构体类型的参数。这些函数可以访问和修改结构体中的数据。
下面是一个简单的示例,展示如何在C语言中定义结构体以及与之相关的函数:
#include <stdio.h>
// 定义结构体
typedef struct {
int id;
char name[50];
float score;
} Student;
// 与结构体相关的函数:显示学生信息
void displayStudent(Student s) {
printf("ID: %d, Name: %s, Score: %.2f\n", s.id, s.name, s.score);
}
// 与结构体相关的函数:修改学生分数
void updateScore(Student *s, float newScore) {
s->score = newScore;
}
int main() {
// 创建一个结构体实例
Student student1 = {1, "Alice", 85.5};
// 调用函数显示学生信息
displayStudent(student1);
// 修改学生分数
updateScore(&student1, 90.0);
// 再次调用函数显示学生信息,此时分数应该已经更新
displayStudent(student1);
return 0;
}
在上面的示例中,我们首先定义了一个名为Student
的结构体,它包含三个成员:id
、name
和score
。然后,我们定义了两个与Student
结构体相关的函数:displayStudent
和updateScore
。displayStudent
函数接受一个Student
类型的参数,并打印出学生的信息。updateScore
函数接受一个指向Student
的指针和一个新的分数,并更新该学生的分数。
在main
函数中,我们创建了一个Student
类型的实例student1
,并初始化了它的成员。然后,我们调用了displayStudent
函数来显示学生的信息。接下来,我们调用了updateScore
函数来修改学生的分数,并再次调用displayStudent
函数来验证分数是否已经被更新。
四、C语言结构体的嵌套
结构体也可以包含其他结构体类型的成员,这被称为结构体的嵌套。嵌套结构体允许我们创建更复杂的数据结构。以下是一个示例:
#include <stdio.h>
struct Address {
char street[100];
char city[50];
char country[50];
};
struct Student {
char name[50];
int age;
float score;
struct Address addr; // 嵌套结构体Address
};
int main() {
struct Student student1 = {
"Tom",
20,
90.5,
{"123 Main St", "Springfield", "USA"} // 初始化嵌套结构体
};
printf("Name: %s, Age: %d, Score: %.1f, Address: %s, %s, %s\n",
student1.name, student1.age, student1.score,
student1.addr.street, student1.addr.city, student1.addr.country);
return 0;
}
在这个例子中,我们定义了一个Address
结构体来存储地址信息,然后在Student
结构体中嵌套了一个Address
类型的成员addr
。在初始化student1
时,我们也初始化了其嵌套的Address
结构体。
五、C语言结构体与函数
结构体通常与函数一起使用,以便对结构体数据进行更复杂的操作。以下是一个示例,展示了如何在函数中传递和修改结构体:
#include <stdio.h>
struct Student {
char name[50];
int age;
float score;
};
void printStudentInfo(struct Student s) {
printf("Name: %s, Age: %d, Score: %.1f\n", s.name, s.age, s.score);
}
void increaseScore(struct Student *s, float increment) {
s->score += increment;
}
int main() {
struct Student student1 = {"Tom", 20, 90.5};
printStudentInfo(student1);
increaseScore(&student1, 5.0);
printStudentInfo(student1);
return 0;
}
在这个例子中,我们定义了两个函数:printStudentInfo
用于打印学生的信息,而increaseScore
则用于增加学生的分数。注意,在increaseScore
函数中,我们使用了一个指向Student
结构体的指针作为参数,以便能够修改传递给函数的结构体变量。
六、C语言结构体练习题
练习题一:定义并初始化学生信息结构体
首先,我们需要定义一个结构体来表示学生的信息。这个结构体可以包含学生的姓名、学号、年龄和成绩。
#include <stdio.h>
#include <string.h>
// 定义学生信息结构体
typedef struct {
char name[20];
int studentID;
int age;
float score;
} Student;
int main() {
// 初始化学生信息
Student stu1 = {"张三", 1001, 20, 90.5};
// 输出学生信息
printf("姓名: %s\n", stu1.name);
printf("学号: %d\n", stu1.studentID);
printf("年龄: %d\n", stu1.age);
printf("成绩: %.1f\n", stu1.score);
return 0;
}
练习题二:创建并打印学生信息数组
现在,我们需要创建一个包含多个学生信息的数组,并遍历数组打印每个学生的信息。
#include <stdio.h>
#include <string.h>
// 定义学生信息结构体(同练习题一)
// ...(省略结构体定义)
int main() {
// 创建学生信息数组
Student students[3] = {
{"李四", 1002, 21, 85.0},
{"王五", 1003, 19, 92.0},
{"赵六", 1004, 20, 88.5}
};
// 遍历数组打印学生信息
for (int i = 0; i < 3; i++) {
printf("学生%d信息:\n", i + 1);
printf("姓名: %s\n", students[i].name);
printf("学号: %d\n", students[i].studentID);
printf("年龄: %d\n", students[i].age);
printf("成绩: %.1f\n", students[i].score);
}
return 0;
}
练习题三:按成绩排序学生信息数组
最后,我们需要对学生信息数组按照成绩进行排序。这里我们使用冒泡排序算法。
#include <stdio.h>
#include <string.h>
// 定义学生信息结构体(同练习题一)
// ...(省略结构体定义)
// 交换两个学生信息的函数
void swap(Student *stu1, Student *stu2) {
Student temp = *stu1;
*stu1 = *stu2;
*stu2 = temp;
}
// 冒泡排序函数
void bubbleSort(Student students[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (students[j].score < students[j + 1].score) {
swap(&students[j], &students[j + 1]);
}
}
}
}
int main() {
// 创建学生信息数组(同练习题二)
// ...(省略数组创建)
// 对学生信息数组进行排序
bubbleSort(students, 3);
// 遍历数组打印排序后的学生信息
printf("按成绩排序后的学生信息:\n");
for (int i = 0; i < 3; i++) {
printf("学生%d信息:\n", i + 1);
printf("姓名: %s\n", students[i].name);
printf("学号: %d\n", students[i].studentID);
printf("年龄: %d\n", students[i].age);
printf("成绩: %.1f\n", students[i].score);
}
return 0;
}
这三个练习题涵盖了C语言中结构体的基本使用,包括定义、初始化、数组操作以及排序。通过练习这些题目,可以加深对C语言中结构体概念的理解和应用。
总结
结构体是C语言中一种强大的工具,允许我们创建自定义的数据类型来组织复杂的数据。通过结构体,我们可以将多个不同类型的数据项组合到一个单独的类型中,并使用.
或->
操作符来访问其成员。此外,结构体还可以嵌套在其他结构体中,或者与函数一起使用来执行更复杂的操作。通过熟练掌握结构体的使用,我们可以更好地组织和操作数据,从而提高程序的可读性和可维护性。