C 语言的结构体
创始人
2025-05-31 01:33:08
0

文章目录

  • 结构体概述
  • 定义结构体类型
  • 定义结构体变量
    • 方法一
    • 方法二
  • 获取或赋给结构体成员的值
  • 结构体数组
    • 定义结构体数组
    • 初始化结构体数组
  • 结构体指针
    • 获取结构体成员
    • 结构体指针作为函数参数

结构体概述

结构体是自定义数据类型, 是由 int, char, float 等基本类型组成的. 可认为结构体是一种聚合类型. 可以将一组类型不同的, 但是用来描述同一件事物的变量放到结构体中, 这样就不必再定义多个变量.

结构体是结构体类型的简称.

定义结构体类型

结构体的定义形式为:

struct 结构体名{结构体所包含的变量(基本类型或派生类型)
};

每个变量都称为结构体的成员. 成员可以是基本类型, 也可以是派生类型.

结构体成员的定义方式与变量和数组的定义方式相同, 只是不能在结构体内初始化. 结构体是定义了一个结构体类型, 结构体成员定义了类型和变量名. 结构体成员不可以初始化, 但结构体变量可以初始化.

注意大括号后面的分号 ; 不能少, 这是一条完整的语句.

struct stu
{int age;float grade;char *name;int id;
};  // struct stu 是一个类型名, 就像 int, short 之类的

定义结构体变量

既然结构体是一种数据类型, 那么就可以用它来定义结构体变量.

stu 就像一个 “模板”, 定义出来的变量都具有相同的性质. 也可以将结构体比作 “图纸”, 将结构体变量比作 “零件”, 根据同一张图纸生产出来的零件的特性都是一样的.

结构体和结构体变量是两个不同的概念: 结构体是一种数据类型, 是一种创建变量的模板, 编译器不会为它分配内存空间, 就像 int, float, char 这些关键字本身不占用内存一样; 结构体变量才包含实实在在的数据, 才需要内存来存储.

方法一

先定义结构体类型, 再定义结构体变量.

struct stu  // 先定义结构体类型
{int age;float grade;char *name;int id;
};  // struct stu 是一个类型名, 就像 int, short 之类的
struct stu stu1, stu2;  // 用定义后的结构体类型定义结构体变量

定义了两个变量 stu1stu2, 它们都是 struct stu 类型, 都由 5 个结构体成员组成. 关键字 struct 不能少.

方法二

也可以在定义结构体的同时定义结构体变量, 将变量放在结构体定义的最后即可.

struct stu
{int age;float grade;char *name;int id;
} stu1, stu2;

如果只需要 stu1, stu2 两个变量, 后面不需要再使用结构体名定义其他变量, 那么在定义时也可以不给出结构体名. 这样做书写简单, 但是因为没有结构体名, 后面就没法用该结构体定义新的变量.

struct
{int age;float grade;char *name;int id;
} stu1, stu2;

理论上结构体的各个成员在内存中是连续存储的, 和数组非常类似, 但是在编译器的具体实现中, 各个成员之间可能会存在缝隙,使得结构体变量所占内存的字节数大于理论数目.

获取或赋给结构体成员的值

结构体和数组一样, 都是一组数据的集合. 数组使用下标 [] 获取单个元素. 结构体使用点号 . 获取单个成员.

获取结构体成员的一般格式为:

结构体变量名.成员名;

通过这种方式可以获取成员的值, 也可以给成员赋值.

程序示例:

#include 
struct
{int age;float grade;char *name;int id;
} stu1, stu2;int main(void)
{stu1.age = 10; // 给成员赋值printf("stu1 age = %d.\n", stu1.age); // 获取成员的值
}

结果:

stu1 age = 10.

第 12 行只能在 main() 函数内, 不能在 main() 函数外.

除了可以对成员进行逐一赋值, 也可以在定义结构体变量时对所有成员整体赋值.

程序示例:

#include 
struct stu
{int age;float grade;char* name;int id;
} stu1, stu2;int main(void)
{struct stu stu1={12, 2, "Tom", 20210003}; // 给成员赋值printf("stu1 age = %d.\n", stu1.age); // 获取成员的值stu1.age = 100;  // 修改成员的值printf("stu1 age = %d.\n", stu1.age); // 获取成员的值return 0;
}

结果:

stu1 age = 12.
stu1 age = 100.

整体赋值仅限于定义结构体变量的时候, 在使用过程中只能对成员逐一赋值. 这和数组赋值非常类似.

整体赋值可以在函数体外进行, 逐一赋值只能在函数体内进行.

struct
{int age;float grade;char *name;int id;
} stu1, stu2 = {12, 90.8, "Tom", 2021}; // 可以在函数体外

程序示例:

#includestruct Book
{char name[20];char id[20];int price;
};int main(void)
{int num = 10;struct Book b = { "C语言","2020",40 };struct Book* pb = &b;//结构体变量名.成员名printf("书名:%s\n", b.name);printf("书号:%s\n", b.id);printf("定价:%d\n", b.price);//结构体指针->成员名printf("书名:%s\n", pb->name);printf("书号:%s\n", pb->id);printf("定价:%d\n", pb->price);//(*结构体指针).成员名printf("书名:%s\n", (*pb).name);printf("书号:%s\n", (*pb).id);printf("定价:%d\n", (*pb).price);return 0;
}

结果:

书名:C语言
书号:2020
定价:40
书名:C语言
书号:2020
定价:40
书名:C语言
书号:2020
定价:40

结构体数组

结构体数组是指数组中的每个元素都是一个结构体, 即元素是结构体的数组.

实际应用中, C 语言结构体数组常被用来表示一个拥有相同数据结构的群体, 比如一个班的具有相同种类信息的多个学生, 一个车间的具有相同种类信息的多个职工等.

定义结构体数组

struct stu
{char *name;	 //姓名int num;	 //学号int age;	 //年龄char group;	 //所在小组float score; //成绩
} class[5];

表示一个班级有 5 个学生.

结构体数组的类型是 struct stu [5] , 数组的元素的类型是结构体类型, 即 struct stu.

初始化结构体数组

方法一:

结构体数组在定义的同时也可以初始化, 例如:

struct stu
{char *name;	 // 姓名int num;	 // 学号int age;	 // 年龄char group;	 // 所在小组float score; // 成绩
} class[5] = {{"Li ping", 5, 18, 'C', 145.0},  // 数组的每一个元素都是一个结构体变量{"Zhang ping", 4, 19, 'A', 130.5},{"He fang", 1, 18, 'A', 148.5},{"Cheng ling", 2, 17, 'F', 139.0},{"Wang ming", 3, 17, 'B', 144.5}
};

方法二:

当对数组中全部元素赋值时, 也可不给出数组长度, 例如:

struct stu
{char *name;	 // 姓名int num;	 // 学号int age;	 // 年龄char group;	 // 所在小组float score; // 成绩
} class[] = {{"Li ping", 5, 18, 'C', 145.0},{"Zhang ping", 4, 19, 'A', 130.5},{"He fang", 1, 18, 'A', 148.5},{"Cheng ling", 2, 17, 'F', 139.0},{"Wang ming", 3, 17, 'B', 144.5}};

获取 Wang ming 的成绩:

class[4].score;  // class[4] 是数组的元素, 是一个结构体变量

修改 Li ping 的学习小组:

class[0].group = 'B';

计算全班学生的总成绩, 平均成绩和以及 140 分以下的人数:

#include struct
{char *name;	 //姓名int num;	 //学号int age;	 //年龄char group;	 //所在小组float score; //成绩
} class[] = {{"Li ping", 5, 18, 'C', 145.0},{"Zhang ping", 4, 19, 'A', 130.5},{"He fang", 1, 18, 'A', 148.5},{"Cheng ling", 2, 17, 'F', 139.0},{"Wang ming", 3, 17, 'B', 144.5}
};int main()
{int i, num_140 = 0;float sum = 0;for (i = 0; i < 5; i++){sum += class[i].score;if (class[i].score < 140)num_140++;}printf("sum=%.2f\naverage=%.2f\nnum_140=%d\n", sum, sum / 5, num_140);return 0;
}

结果:

sum=707.50
average=141.50
num_140=2

结构体指针

即指向结构体变量的指针.

C 语言结构体指针的定义形式一般为:

struct 结构体名 *指针变量名;

指针的类型是 struct 结构体名 *, 指向的元素的类型是 struct 结构体名. 指向的是某个结构体变量而不是指向结构体类型.

实例:

//结构体类型
struct stu
{char *name;	 //姓名int num;	 //学号int age;	 //年龄char group;	 //所在小组float score; //成绩
} stu1 = {"Tom", 12, 18, 'A', 136.5};
//结构体指针
struct stu *pstu = &stu1;

可以在定义结构体的同时定义结构体指针:

struct stu
{char *name;	 // 姓名int num;	 // 学号int age;	 // 年龄char group;	 // 所在小组float score; // 成绩
} stu1 = {"Tom", 12, 18, 'A', 136.5}, *pstu = &stu1;

结构体变量名不会被转换为指针, 无论在任何表达式中它表示的都是整个结构体变量本身, 要想取得结构体变量的地址, 必须在结构体变量前面加 &.

不可能去取一个结构体类型名的地址, 也不能将它赋值给其他变量,下面的写法是错误的.

struct stu *pstu = &stu;
struct stu *pstu = stu;

获取结构体成员

获取结构体变量的成员的方法:

  1. 结构体变量.成员
  2. 指向结构体变量的指针 -> 成员
  3. (*指向结构体变量的指针).成员

通过结构体指针可以获取指针指向的结构体变量的结构体成员, 一般形式为:

(*pointer).memberName

或者:

pointer->memberName

第一种写法中, . 的优先级高于 *, (*pointer) 两边的括号不能少. 如果去掉括号写作 *pointer.memberName, 那么就等效于 *(pointer.memberName), 这样意义就完全不对了.

第二种写法中, -> 是一个新的运算符, 习惯称它为 “箭头”, 可以通过结构体指针直接取得结构体变量的成员; 这也是 -> 在 C 语言中的唯一用途.

上面的两种写法是等效的, 通常采用后面的写法, 这样更加直观.

#include int main(void)
{struct{char *name;int num;int age;char group;float score;} stu1 = {"Tom", 12, 18, 'A', 136.5}, *pstu = &stu1;printf("%s的学号是%d, 年龄是%d, 在%c组, 今年的成绩是%.1f.\n", (*pstu).name, (*pstu).num, (*pstu).age, (*pstu).group, (*pstu).score);printf("%s的学号是%d, 年龄是%d, 在%c组, 今年的成绩是%.1f.\n", pstu->name, pstu->num, pstu->age, pstu->group, pstu->score);return 0;
}

结果:

Tom的学号是12, 年龄是18, 在A组, 今年的成绩是136.5.
Tom的学号是12, 年龄是18, 在A组, 今年的成绩是136.5.

结构体数组指针: 指向结构体数组的指针.

#include struct stu
{char *name;int num;int age;char group;float score;
} stus[] = {{"Zhou ping", 5, 18, 'C', 145.0},{"Zhang ping", 4, 19, 'A', 130.5},{"Liu fang", 1, 18, 'A', 148.5},{"Cheng ling", 2, 17, 'F', 139.0},{"Wang ming", 3, 17, 'B', 144.5}}, *ps;int main(void)
{int len = sizeof(stus) / sizeof(struct stu);  // 求出结构体数组的元素的个数printf("Name\t\tNum\t\tAge\t\tGroup\tScore\t\n");for (ps = stus; ps < stus + len; ps++){printf("%s\t%d\t%d\t%c\t\t%.1f\n", ps->name, ps->num, ps->age, ps->group, ps->score);}return 0;
}

结果:

Name            Num             Age             Group   Score
Zhou ping       5       18      C               145.0
Zhang ping      4       19      A               130.5
Liu fang        1       18      A               148.5
Cheng ling      2       17      F               139.0
Wang ming       3       17      B               144.5

struct stu * 类型的指针指向元素的类型为 struct stu 的数组.

即数组的元素的类型为 struct stu, 数组的类型为 struct stu [5].

类似于 int * 类型的指针指向元素类型为 int, 数组类型为 int [5] 的数组.

程序示例:

#include 
struct Book
{char name[20];short price;
};int main(void)
{struct Book b1 = { "C程序设计", 55 };struct Book* pb = &b1;  // 指向结构体变量的指针printf("%s\n", (*pb).name);  // 使用指向结构体变量的指针获取该变量的成员的值printf("%d\n", (*pb).price);printf("%s\n", pb->name);  // 使用指向结构体变量的指针获取该变量的成员的值printf("%d\n", pb->price);return 0;
}

结果:

C程序设计
55
C程序设计
55

结构体指针作为函数参数

结构体变量名代表的是整个集合本身, 作为函数参数时传递的整个集合, 也就是所有成员, 而不是像数组一样被编译器转换成一个指针. 如果结构体成员较多, 尤其是成员为数组时, 传送的时间和空间开销会很大, 影响程序的运行效率. 最好用结构体指针, 实参传向形参的是一个地址, 非常快速.

程序示例:

// 计算全班学生的总成绩、平均成绩和以及 140 分以下的人数
#include struct stu
{char *name;int num;int age;char group;float score;
} stus[] = {{"Zhou ping", 5, 18, 'C', 145.0},{"Zhang ping", 4, 19, 'A', 130.5},{"Liu fang", 1, 18, 'A', 148.5},{"Cheng ling", 2, 17, 'F', 139.0},{"Wang ming", 3, 17, 'B', 144.5}
};void average(struct stu *ps, int len);int main(void)
{int len = sizeof(stus) / sizeof(struct stu);  // 结构体数组的长度average(stus, len);return 0;
}void average(struct stu *ps, int len)
{int i, num_140 = 0;float average, sum = 0.0f;for (i = 0; i < len; i++){sum += (ps + i)->score;if ((ps + i)->score < 140)num_140++;}printf("sum=%.2f\naverage=%.2f\nnum_140=%d\n", sum, sum / 5, num_140);
}

结果:

sum=707.50
average=141.50
num_140=2

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...