C语言struct结构体的定义和使用(非常详细)
结构体(struct)是 C语言中的一种自定义复合数据类型,它允许将多个不同类型的数据元素(称为成员)组织在一起。
结构体可用于存储具有多个属性的实体,例如,一个人员可能具有姓名、薪水等属性。这些相关属性可以通过结构体在一个数据结构中进行组织和管理,从而提高代码的可读性和维护性。
尽管结构体类型的定义较长,但其实它与 int 类型类似。正如在 int 后填写变量名可以声明一个整型变量一样,在结构体类型后添加变量名也可以声明一个结构体变量。
要访问结构体的各个成员,需要使用成员运算符
接下来,我们为 timmy 变量的各个成员赋值:
有些读者可能会疑惑,为什么需要使用 strcpy() 函数为 timmy.name 赋值,而不是直接将其写成 timmy.name = "Timmy" 呢?
在这段代码中,timmy.name = "Timmy"; 的写法是错误的,因为 timmy.name 是一个字符数组,而不是一个字符指针。字符数组的内容不能直接使用赋值运算符进行赋值。要将字符串常量分配给字符数组,需要使用 strcpy() 函数或其他适当的字符串复制方法。
当然可以,我们只需要给结构体类型定义一个别名,例如:
事实上,我们还可以在最开始时进行结构体类型声明。这样,所有的结构体变量都可以使用该别名进行声明。这相当于先定义一个模板,然后使用该模板生成各个变量:
需要注意的是,如果结构体类型是在某个函数中声明的,那么其别名只能在该函数内部使用,例如:
如果需要在多个函数中使用结构体别名,可以将结构体声明放到函数外面,例如:
例如:
对于第三点,person 结构体成员声明的顺序应依次为 name、gender、height、weight,对应的初始化列表中的初始化值顺序应为 "timmy"、1、170.00、60.00。这点需要严格执行。
以下是正确和错误的结构体变量初始化方式:
因此,我们需要严格按照成员声明的顺序,对初始化列表中的初始化值进行排列。
结构体数组与基本类型数组类似,通过在方括号内填写数组元素的数量来声明。初始化列表也可用于初始化结构体数组,初始化列表中依次填每个结构体的初始化列表,每个结构体的初始化列表之间用逗号分隔。
我们可以通过在方括号内填写下标来访问结构体数组中的元素。同样地,下标也是从 0 开始的。
例如,我们可以声明一个用于存储通信方式的结构体:
现在,我们需要记录每个人员的通信方式。我们可以将上述的结构体添加到人员结构体中,作为其一个成员:
在人员信息中,通信方式结构体为其第五个成员。因此,在人员信息初始化列表的第五个位置处,填写通信方式结构体的初始化列表,即可正确地对结构体进行初始化。
使用 . 加上字段名可以访问通信方式结构体的成员。如果想要访问其内部的成员,可以再次使用 . 加上字段名,例如:
取出结构体指针值的操作,也和之前的操作类似。由于取地址&与取值 * 具有可逆关系,我们可以把指针先转为结构体再使用。
另外,C语言提供了更加方便的写法,即成员间接运算符 ->。(*pTimmy).name 等价于 pTimmy->name,例如:
传值方式会将整个结构体的副本传递给函数,而传指针方式只会传递结构体的地址。
下面程序展示了一个通过传值方式将结构体传递给函数的示例。
在函数中传递结构体时,使用传值方式有以下几个潜在问题:
1) 性能消耗:当结构体较大时,传值方式会将整个结构体的副本传递给函数。这会导致更多的内存和CPU时间被消耗在复制结构体数据上。相比之下,传指针方式只需传递结构体的地址,无论结构体有多大,都只需传递一个指针大小的数据。
2) 原始结构体不会被修改:由于传值方式传递的是结构体的副本,函数内对结构体的修改不会影响到原始结构体。这在某些情况下可能不是你想要的结果,尤其是当你需要在函数内修改原始结构体时。传指针方式可以解决这个问题,因为它传递的是原始结构体的地址。
综上所述,尽管传值方式在结构体较小时是可行的,但在许多情况下,传指针方式更为有效和安全。传指针方式可以减少内存和 CPU 时间消耗,允许在函数内修改原始结构体,并避免潜在的错误。
下面程序展示了一个通过传指针方式将结构体传递给函数的示例。
结构体可用于存储具有多个属性的实体,例如,一个人员可能具有姓名、薪水等属性。这些相关属性可以通过结构体在一个数据结构中进行组织和管理,从而提高代码的可读性和维护性。
尽管结构体类型的定义较长,但其实它与 int 类型类似。正如在 int 后填写变量名可以声明一个整型变量一样,在结构体类型后添加变量名也可以声明一个结构体变量。
struct {
char name[20];
int gender;
double height;
double weight;
}timmy;
timmy 是由该结构体声明的变量,它包含 4 个成员。要访问结构体的各个成员,需要使用成员运算符
.与成员名:
timmy.name; timmy.gender; timmy.height; timmy.weight;
接下来,我们为 timmy 变量的各个成员赋值:
strcpy(timmy.name, "Timmy"); timmy.gender = 1; timmy.height = 170.00; timmy.weight = 60;
有些读者可能会疑惑,为什么需要使用 strcpy() 函数为 timmy.name 赋值,而不是直接将其写成 timmy.name = "Timmy" 呢?
在这段代码中,timmy.name = "Timmy"; 的写法是错误的,因为 timmy.name 是一个字符数组,而不是一个字符指针。字符数组的内容不能直接使用赋值运算符进行赋值。要将字符串常量分配给字符数组,需要使用 strcpy() 函数或其他适当的字符串复制方法。
C语言结构体别名
现在,我们想要定义多个人员信息的结构体变量,例如:
struct {
char name[20];
int gender;
double height;
double weight;
} timmy;
struct {
char name[20];
int gender;
double height;
double weight;
} david;
struct {
char name[20];
int gender;
double height;
double weight;
} jane;
上述代码使用结构体定义了 timmy、david、jane 三个变量。由于这三个结构体变量的内部成员都是一致的,每次声明都要写一段很长的代码,这是非常烦琐的,因此我们是否可以只声明一次结构体类型,然后重复使用它呢?当然可以,我们只需要给结构体类型定义一个别名,例如:
struct person {
char name[20];
int gender;
double height;
double weight;
} timmy;
struct person david;
struct person jane;
在这段代码中,第一次声明结构体变量时,我们在 struct 和 { 之间填写了一个结构体别名。如果以后需要使用这种结构,则只需使用 struct 加上该别名即可声明该结构体的变量。事实上,我们还可以在最开始时进行结构体类型声明。这样,所有的结构体变量都可以使用该别名进行声明。这相当于先定义一个模板,然后使用该模板生成各个变量:
struct person {
char name[20];
int gender;
double height;
double weight;
};
struct person timmy;
struct person david;
struct person jane;
需要注意的是,如果结构体类型是在某个函数中声明的,那么其别名只能在该函数内部使用,例如:
void func1()
{
struct person{
char name[20];
int gender;
double height;
double weight;
};
struct person timmy;
}
void func2()
{
// 别名 person 无法在 func2 中使用
struct person david;
}
在上述代码中,函数 func1() 声明了一个结构体类型,它的别名为 person。同时,函数 func1() 使用该别名声明了一个结构体变量 timmy。函数 func2() 使用别名 person 声明了另一个结构体变量 david,但是别名 person 无法在函数 func2() 中使用,因此代码会编译报错。如果需要在多个函数中使用结构体别名,可以将结构体声明放到函数外面,例如:
// 将结构体声明放到函数外
struct person {
char name[20];
int gender;
double height;
double weight;
};
void func1()
{
struct person timmy;
}
void func2()
{
struct person david;
}
C语言结构体的初始化
初始化结构体是为结构体中的成员分配初始值的过程。在定义结构体变量时,我们可以使用花括号({})为成员分配初始值。成员的初始化顺序应与结构体定义中的成员顺序一致。例如:
struct person timmy = {"timmy", 1, 170.00, 60.00};
结构体变量初始化的形式与数组初始化的形式类似。在声明时,结构体变量后跟等号 = 和初始化列表。结构体的初始化列表需要注意以下四点:
- 初始化列表由 {} 包括;
- {} 内是结构体成员需要被初始化的值;
- 初始化值应按照声明结构体成员的顺序依次排列;
- 每个初始化值之间应用逗号(,)分隔。
对于第三点,person 结构体成员声明的顺序应依次为 name、gender、height、weight,对应的初始化列表中的初始化值顺序应为 "timmy"、1、170.00、60.00。这点需要严格执行。
以下是正确和错误的结构体变量初始化方式:
// 正确的初始化方式
struct person timmy = {"timmy", 1, 170.00, 60.00};
// 错误的初始化方式
struct person timmy = {1, "timmy", 170.00, 60.00}; // 类型不一致无法编译通过
struct person timmy = {"timmy", 1, 60.00, 170.00}; // 编译可以通过,但是身高和体重数据被颠倒了
在上述代码中:
- 第一个结构体变量的初始化列表顺序正确;
- 第二个结构体变量的初始化列表顺序错误,因为第一个初始化值是一个整数,而不是字符数组,这将导致编译错误;
- 第三个结构体变量的初始化列表可以编译通过,但是身高和体重数据被颠倒了。
因此,我们需要严格按照成员声明的顺序,对初始化列表中的初始化值进行排列。
C语言结构体数组
结构体数组由多个结构体类型的元素组成。在 C语言中,我们可以像处理其他数据类型的数组一样处理结构体数组。例如:
struct person {
char name[20];
int gender;
double height;
double weight;
};
struct person people[3] = {
{"timmy", 1, 170.00, 60.00},
{"david", 1, 175.00, 65.00},
{"jane", 2, 165.00, 55.00}
};
for(int i = 0; i < 3; i++) {
struct person per = people[i];
printf("%s ", per.name);
printf("%d ", per.gender);
printf("%.2f ", per.height);
printf("%.2f\n", per.weight);
}
这段代码定义并初始化了一个大小为 3 的结构体数组 people,之后使用 for 循环输出了数组内的值。结构体数组与基本类型数组类似,通过在方括号内填写数组元素的数量来声明。初始化列表也可用于初始化结构体数组,初始化列表中依次填每个结构体的初始化列表,每个结构体的初始化列表之间用逗号分隔。
我们可以通过在方括号内填写下标来访问结构体数组中的元素。同样地,下标也是从 0 开始的。
C语言结构体嵌套
在 C语言中,结构体可以嵌套,也就是说,一个结构体可以包含另一个结构体作为其成员。这样做可以更好地表示复杂的数据结构。例如,我们可以声明一个用于存储通信方式的结构体:
struct contact {
char phone[20];
char email[20];
};
现在,我们需要记录每个人员的通信方式。我们可以将上述的结构体添加到人员结构体中,作为其一个成员:
struct person{
char name[20];
int gender;
double height;
double weight;
struct contact c;
};
在人员信息中,通信方式结构体为其第五个成员。因此,在人员信息初始化列表的第五个位置处,填写通信方式结构体的初始化列表,即可正确地对结构体进行初始化。
struct person timmy = {
"timmy", 1, 170.00, 60.00, {"130123456678", "timmy@xxx.com"}
};
使用 . 加上字段名可以访问通信方式结构体的成员。如果想要访问其内部的成员,可以再次使用 . 加上字段名,例如:
struct person timmy = {
"timmy", 1, 170.00, 60.00, {"130123456678", "timmy@xxx.com"}
};
printf("%s\n", timmy.c.phone);
printf("%s\n", timmy.c.email);
这样就可以分别输出timmy的电话号码和电子邮件地址。C语言结构体指针
在 C语言中,我们可以使用指针指向结构体。结构体指针可以用于间接访问结构体成员,以及将结构体作为函数参数或返回值进行传递,例如:
struct person timmy = {"timmy", 1, 170.00, 60.00};
struct person *pTimmy = &timmy;
和往常一样,加上星号(*)用于声明一个指针。我们可以使用取地址运算符 & 获取指针。取出结构体指针值的操作,也和之前的操作类似。由于取地址&与取值 * 具有可逆关系,我们可以把指针先转为结构体再使用。
printf("%s\n", (*pTimmy).name);
printf("%d\n", (*pTimmy).gender);
printf("%.2f\n", (*pTimmy).height);
printf("%.2f\n", (*pTimmy).weight);
由于成员运算符 . 的优先级高于取值 *,为了让取值 * 先运算,必须使用括号将 *pTimmy 包括起来。另外,C语言提供了更加方便的写法,即成员间接运算符 ->。(*pTimmy).name 等价于 pTimmy->name,例如:
printf("%s\n", pTimmy->name);
printf("%d\n", pTimmy->gender);
printf("%.2f\n", pTimmy->height);
printf("%.2f\n", pTimmy->weight);
使用成员间接运算符 -> 可以更加简洁地访问结构体指针的成员。C语言结构体作为函数参数
在 C语言中,我们可以将结构体作为函数参数或返回值进行传递。通常有两种方式:传值和传指针。传值方式会将整个结构体的副本传递给函数,而传指针方式只会传递结构体的地址。
下面程序展示了一个通过传值方式将结构体传递给函数的示例。
#include <stdio.h>
#include <string.h>
struct person {
char name[20];
int gender;
double height;
double weight;
};
void change(struct person per)
{
strcpy(per.name, "david");
per.gender = 1;
per.height = 175.00;
per.weight = 65.00;
}
int main()
{
struct person timmy = { "timmy", 1, 170.00, 60.00 };
change(timmy);
printf("%s\n", timmy.name);
printf("%d\n", timmy.gender);
printf("%.2f\n", timmy.height);
printf("%.2f\n", timmy.weight);
return 0;
}
在上述代码中,函数 change() 被调用时,参数 per 是通过传值方式进行传递的。由于函数内的修改只会影响传入的副本而不是原始结构体,因此函数 change() 对结构体 timmy 所做的修改不会被保留。因此,程序的输出结果如下所示,仍然是原始的 timmy 的数据。
timmy
1
170.00
60.00
在函数中传递结构体时,使用传值方式有以下几个潜在问题:
1) 性能消耗:当结构体较大时,传值方式会将整个结构体的副本传递给函数。这会导致更多的内存和CPU时间被消耗在复制结构体数据上。相比之下,传指针方式只需传递结构体的地址,无论结构体有多大,都只需传递一个指针大小的数据。
2) 原始结构体不会被修改:由于传值方式传递的是结构体的副本,函数内对结构体的修改不会影响到原始结构体。这在某些情况下可能不是你想要的结果,尤其是当你需要在函数内修改原始结构体时。传指针方式可以解决这个问题,因为它传递的是原始结构体的地址。
综上所述,尽管传值方式在结构体较小时是可行的,但在许多情况下,传指针方式更为有效和安全。传指针方式可以减少内存和 CPU 时间消耗,允许在函数内修改原始结构体,并避免潜在的错误。
下面程序展示了一个通过传指针方式将结构体传递给函数的示例。
#include <stdio.h>
#include <string.h>
struct person{
char name[20];
int gender;
double height;
double weight;
};
void change(struct person *per)
{
strcpy(per->name, "david");
per->gender = 1;
per->height = 175.00;
per->weight = 65.00;
}
int main()
{
struct person timmy = {"timmy", 1, 170.00, 60.00};
change(&timmy);
printf("%s\n", timmy.name);
printf("%d\n", timmy.gender);
printf("%.2f\n", timmy.height);
printf("%.2f\n", timmy.weight);
return 0;
}
在上述代码中,函数 change() 被调用时,参数 per 是通过传指针方式进行传递的。由于函数内修改的是原始结构体,因此函数 change() 对结构体 timmy 所做的修改会得到保留。程序的输出结果如下:
timmy
1
170.00
60.00
C语言结构体实例
编写一个程序,实现一个简单的学生信息管理系统,用于存储和输出学生的姓名、年龄和成绩。要求使用结构体和结构体数组来实现。
#include <stdio.h>
// 定义学生信息结构体
struct Student {
char name[30];
int age;
float score;
};
// 定义一个函数,用于输出学生信息
void printStudentInfo(const struct Student* student) {
printf("姓名:%s\n", student->name);
printf("年龄:%d\n", student->age);
printf("成绩:%.2f\n", student->score);
}
int main() {
// 创建一个结构体数组,用于存储学生信息
struct Student students[] = {
{"Alice", 20, 89.5},
{"Bob", 21, 78.0},
{"Charlie", 22, 95.5},
};
// 获取数组的长度
int numberOfStudents = sizeof(students) / sizeof(students[0]);
// 遍历结构体数组,输出学生信息
for (int i = 0; i < numberOfStudents; i++) {
printf("学生 #%d\n", i + 1);
printStudentInfo(&students[i]);
printf("\n");
}
return 0;
}
ICP备案:
公安联网备案: