C语言struct结构体的定义和使用(非常详细)

C语言struct结构体的定义和使用(非常详细)

结构体(struct)是 C语言中的一种自定义复合数据类型,它允许将多个不同类型的数据元素(称为成员)组织在一起。

结构体可用于存储具有多个属性的实体,例如,一个人员可能具有姓名、薪水等属性。这些相关属性可以通过结构体在一个数据结构中进行组织和管理,从而提高代码的可读性和维护性。

尽管结构体类型的定义较长,但其实它与 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

#include

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

#include

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

// 定义学生信息结构体

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;

}

相关

510221四川省长寿县身份证地区编号身份证查询
365bet中文官网

510221四川省长寿县身份证地区编号身份证查询

📅 10-16 👁️ 3679
摆脱黑色底色的图片打印问题
mobile3656

摆脱黑色底色的图片打印问题

📅 10-13 👁️ 9286
微信公众号文章导出工具 100%还原原文样式:wechat-article-exporter