Logo

郎哥编程

使用结构体

2024-05-15 35

在C语言里面,我们学过的数据基础类型有char、short、int、long等,并且还学习了通过数组来构造数据的集合。但是在不同的情况下,我们往往需要一些不同的数据类型组合成一种新的数据结构。

例如:编写一个学生信息管理程序时,就需要存储学生的信息,学生的信息包括学生名称、学号、性别、年龄等数据。学生信息都是不同类型的数据,描述一个学生的信息需要定义(声明)多个不同数据类型的变量,同时还需要一个数组来描述多名学生的信息。

结构体的定义

C语言提供了一种可以自定义的数据类型,用来描述类似学生信息这样的结构化数据。定义结构体的语法为:

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

其中,struct是定义结构体的关键字,结构体名是结构体的名称,是合法的C语言标识符。结构体主体使用一对大括号封闭起来,在结构体主体内,可以定义多个结构体成员,每个结构体成员定义方式和变量的声明方式相同,成员之间用分隔符“;”隔开。

例如:学生信息结构体。

struct  STUDENT{
	// 学生姓名
	char name[30];
	// 年龄
	int age;
};

上面定义了一个名称为STUDENT的结构体,定义结构体名称时,一般全部采用大写,当然也可以按照自己的喜好来命名,全部采用大写可以和变量名称区分开,结构体有name、age两个成员。

定义的结构体只是一种自定义的数据类型,结构体的使用和其它数据类型的使用完全相同,需要在程序中定义一个结构体变量。

例如:下面定义了结构体STUDEN的结构体变量student,STUDEN是结构体,student是结构体变量。

STUDEN  student;

也可以在定义结构体时,直接定义一个结构体变量。

struct  STUDENT{
	// 学生姓名
	char name[30];
	// 年龄
	int age;
} student;

为了访问结构体的成员,C语言提供了成员访问运算符“.”。成员访问运算符是结构体变量名称和要访问的结构成员之间的一个“.”符号,案例8-1演示了访问结构体成员的使用方法。

例1  使用结构体练习

程序清单 sample.c

#include <stdio.h>
#include <string.h>
// 定义学生信息结构体
struct  STUDENT{
	// 学生姓名
	char name[30];
	// 年龄
	int age;
} student;
// 程序主函数
void main()
{
	// 初始化结构体变量student
	// 访问结构体成员使用“.”运算符
	student.age = 21;
	strcpy(student.name,"马汉");
	// 输出结构体变量student
	printf("姓名:%s\n年龄:%d\n",student.name,student.age);
}

例8-1定义了结构体STUDENT,并声明了结构体变量student,在main()函数内初始化结构体变量student,并输出结构体变量成员信息。

结构体数组

数组的元素也可以是结构类型,因此可以声明结构体数组,结构体数组的每一个元素都是具有相同结构类型的变量。在实际应用中,经常用结构数组来表示具有相同数据结构的数据集合。例如:学习信息管理程序的学生信息、图书管理系统的图书信息等。

声明结构体数组变量的语法和声明其它数据类数组的语法相同,声明结构体数组变量时,一般要指定数组的长度,然后在程序中对数组元素进行赋值。

例如,可以创建一个包含10个学生的结构体数组:

struct STUDENT students[10];

这个students数组包含了10个STUDENT结构体,可以通过数组索引来访问或修改数组内的结构体元素,

你可以通过数组索引来访问和修改这些结构体中的成员。下面的代码设置第一个学生的名字、年龄:

students[0].age = 20;
students[0].name = "张三";

例2  遍历结构体数组元素

程序清单

#include <stdio.h>
#include <string.h>
// 定义学生结构体
typedef struct {
    char name[50];
    int age;
    float score;
} Student;
int main() {
 	int i;
	// 初始化学生数组
    struct STUDENT students[3] = {
        {"张三", 20},
        {"李四", 21},
        {"王五", 19}
    };
    // 遍历学生数组并打印信息
    printf("学生信息:\n");
    for(i = 0; i < 3; i++) {
        printf("姓名:%s,年龄:%d", students[i].name, students[i].age);
	}
	return 0;
}

案例代码定义了一个名称为STUDENT的结构体,其中包含学生的姓名(一个字符数组)、年龄(一个整数)。然后,在main函数中,创建一个包含3个学生的STUDENT结构体数组,并初始化每个学生的信息。使用一个for循环遍历这个数组,并使用printf函数打印出每个学生的姓名和年龄。

结构体指针

结构体指针是指向结构体变量的指针,通过结构体指针可以间接地访问和修改结构体中的数据成员。

定义一个结构体指针的基本语法如下:

struct 结构体名 *指针名;

例如前面定义的STUDENT结构体,可以这样定义一个指向STUDENT结构体变量的指针:

struct STUDENT *studentPtr;

上面定义的结构体指针变量studentPtr指向一个随机的内存地址,需要对studentPtr进行初始化后,才能使用该结构体指针变量。初始化代码如下:

struct STUDENT *studentPtr;
struct STUDENT student = {"Tom", 20};
studentPtr = &student;
printf("姓名:%s\n年龄:%d\n",studentPtr->name,studentPtr->age);

上面的初始化代码先定义一个STUDENT结构体变量student,然后将student的内存地址赋值给结构体指针变量studentPtr。

结构体指针变量还有一种初始化方法,就是采用内存申请的方法为结构体指针变量直接分配内存,在《内存管理》章节会使用这种方法。案例【8-3】使用了这种方法。

例3  结构体指针应用示例

程序清单

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义学生结构体
typedef struct {
    char name[50];
    int age;
    float score;
} Student;
// 创建学生
Student* createStudent(const char* name, int age, float score) {
    Student* student = (Student*)malloc(sizeof(Student));
    if (student == NULL) {
        printf("内存分配失败!\n");
        exit(1);
    }
    strcpy(student->name, name);
    student->age = age;
    student->score = score;
    return student;
}
// 打印学生信息
void printStudent(Student* student) {
    printf("姓名:%s,年龄:%d,成绩:%.2f\n", student->name, student->age, student->score);
}
// 释放学生内存
void freeStudent(Student* student) {
    if (student != NULL) {
        free(student);
        student = NULL;
    }
}
int main() {
    // 创建学生结构体指针
    Student* student1 = createStudent("张三", 20, 85.5);
    Student* student2 = createStudent("李四", 21, 92.0);
    // 打印学生信息
    printStudent(student1);
    printStudent(student2);
    // 释放学生内存
    freeStudent(student1);
    freeStudent(student2);
    return 0;
}

案例代码定义了三个函数:

createStudent:该函数接受学生的姓名、年龄和成绩作为参数,并动态地分配内存来创建一个新的学生结构体。然后设置结构体的字段并返回指向该结构体的指针。

printStudent:该函数接受一个指向学生结构体的指针作为参数,并打印出学生的信息。

freeStudent:该函数接受一个指向学生结构体的指针作为参数,并释放该结构体所占用的内存。

在main函数内创建了两个学生结构体指针student1和student2,并使用createStudent函数来动态地创建两个学生对象。然后调用printStudent函数来打印这两个学生的信息。最后使用freeStudent函数来释放这两个学生对象所占用的内存。

例3展示了如何使用结构体指针来动态地管理学生对象的创建和销毁。在实际应用中,该方法可以更加灵活地处理大量数据,并且可以在需要时动态地分配和释放内存。

结构体作为函数参数

结构体作为函数参数时,实际上传递的是结构体的一个副本,这意味着函数内对结构体的任何修改都不会影响到原始的结构体。若需要函数对原始结构体进行修改,可以传递结构体指针。

结构体作为函数参数时,存在值传递和指针传递两种方式。

按值传递:在这种情况下,函数接收结构体的一个副本。因此,函数内对结构体的修改不会影响原始的结构体。

指针传递:在这种情况下,函数接收结构体的指针。因此,函数内对结构体的修改会直接影响到原始的结构体。

例4  结构体作为值传递

#include <stdio.h>
typedef struct {
    int id;
    char name[50];
} Student;
void printStudent(Student s) {
    printf("ID: %d, Name: %s\n", s.id, s.name);
}
int main() {
    Student stu = {1, "Alice"};
    printStudent(stu);
    return 0;
}
typedef struct {
    int id;
    char name[50];
} Student;
void printStudent(Student s) {
    printf("ID: %d, Name: %s\n", s.id, s.name);
}
int main() {
    Student stu = {1, "Alice"};
    printStudent(stu);
    return 0;
}

当结构体很大时,按值传递可能会导致大量的数据复制,从而影响性能。

例5  结构体作为指针传递

#include <stdio.h>
#include <string.h>
typedef struct {
    int id;
    char name[50];
} Student;
void updateStudent(Student *s) {
    s->id = 2;
    strcpy(s->name, "Bob");
}
int main() {
    Student stu = {1, "Alice"};
    printf("Before update: ID: %d, Name: %s\n", stu.id, stu.name);
    updateStudent(&stu);
    printf("After update: ID: %d, Name: %s\n", stu.id, stu.name);
    return 0;
}

指针传递效率更高,因为它只传递一个地址,而不是整个结构体。但也带来了额外的风险,因为函数可以修改原始的结构体。

代码在线纠错(通义千问 qwen-max)

支持粘贴多个代码文件,提交后由阿里云通义千问自动分析代码漏洞、语法错误、逻辑问题并给出修改建议。
您已解锁 AI 代码纠错功能,可正常使用!

评论区

登录 后发表评论
暂无评论