结构体类型的概念及定义
构造类型:不是基本类型的数据结构也不是指针,它是若干个相同或不同类型的数据构成的集合,常用的构造类型有数组、结构体、共用体。
数组用于保存多个相同类型的数据。
结构体用于保存多个不同类型的数据。
结构体的概念
结构体是一种构造类型的数据结构。
是一种或多种基本类型或构造类型的数据的集合。
结构体类型的定义
先定义结构体类型,再去定义结构体变量
1 2 3 4 5
| struct 结构体名 { 成员; } 结构体变量1, 结构体变量2;
struct 结构体名 变量3, 变量4;
|
如:
1 2 3 4 5 6 7
| struct stu { int num; char name[20]; char sex; } lucy, bob, lilei;
struct stu xiaohong, xiaoming;
|
注意:一般在全局定义结构体。
无名结构体的定义
在定义结构体类型的时候,没有结构体类型名,顺便定义结构体变量,因为没有类型名,所以以后不能再定义相关类型的数据。
1 2 3
| struct { 成员列表; } 变量1, 变量2;
|
例如:
1 2 3 4 5
| struct { int num; char name[20]; char sex; } lucy, bob;
|
给结构体类型取别名
这是经常做的事情。
1 2 3
| typedef struct 结构体名 { 成员列表; } 别名;
|
注意:typedef
给结构体起了一个别名,在使用别名定义结构体变量变量时不要加上
strcut
.
例如:
1 2 3 4 5
| typedef struct stu { int num; char name[20]; char sex; } STU;
|
STU lucy;
和 struct stu;
是等价的。
结构体变量的定义初始化及使用
结构体变量的定义和初始化
结构体变量,是个变量,这个变量是若干个数据的集合。
- 在定义结构体变量之前首先得有结构体类型。
- 在定义结构体变量的时候,可以顺便给结构体变量赋初值。
- 结构体变量初始化的时候,各个成员顺序初始化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <stdio.h>
struct stu { int id; char name[32]; char sex; int age; } zhangsan, lisi = {1002, "李四", 'B', 25};
typedef struct { int a; int b; char c; } MSG;
int main(int argc, char *argv[]) { struct stu wangwu; struct stu zhaoliu = {1001, "赵六", 'B', 20}; MSG msg1, msg2 = {100, 200, 'w'}; return 0; }
|
结构体变量的使用
结构体变量对成员调用的方式:
结构体变量的简单使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <stdio.h> #include <string.h>
struct stu { int id; char name[32]; char sex; int age; } zhangsan, lisi = {1002, "李四", 'B', 25};
typedef struct { int a; int b; char c; } MSG;
int main(int argc, char *argv[]) { struct stu wangwu; struct stu zhaoliu = {1001, "赵六", 'B', 20}; MSG msg1, msg2 = {100, 200, 'w'}; zhangsan.id = 1001; strcpy(zhangsan.name, "张三"); zhangsan.sex = 'B'; zhangsan.age = 18; printf("%d - %s - %c - %d\n", zhangsan.id, zhangsan.name, zhangsan.sex, zhangsan.age); printf("%d - %s - %c - %d\n", lisi.id, lisi.name, lisi.sex, lisi.age); printf("%d - %s - %c - %d\n", zhaoliu.id, zhaoliu.name, zhaoliu.sex, zhaoliu.age); printf("%d - %d - %c\n", msg2.a, msg2.b, msg2.c); return 0; }
|
输出结果:
image
在结构体中嵌套结构体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include <stdio.h> #include <string.h>
typedef struct { int year; int month; int day; } BD;
typedef struct { int id; char name[32]; BD birthday; } STU;
int main() { STU xiaoming; xiaoming.id = 1001; strcpy(xiaoming.name, "小明"); xiaoming.birthday.year = 2002; xiaoming.birthday.month = 12; xiaoming.birthday.day = 20; printf("%d - %s - %d-%d-%d\n", xiaoming.id, xiaoming.name, xiaoming.birthday.year, xiaoming.birthday.month, xiaoming.birthday.day); STU xiaoli = {1002, "小丽", 2000, 1, 1}; printf("%d - %s - %d-%d-%d\n", xiaoli.id, xiaoli.name, xiaoli.birthday.year, xiaoli.birthday.month, xiaoli.birthday.day); return 0; }
|
输出结果:
image
相同类型的结构体变量可以相互赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <stdio.h> #include <string.h>
struct stu { int id; char name[32]; char sex; int age; };
int main(int argc, char *argv[]) { struct stu zhangsan; zhangsan.id = 1001; strcpy(zhangsan.name, "张三"); zhangsan.sex = 'B'; zhangsan.age = 18; printf("%d - %s - %c - %d\n", zhangsan.id, zhangsan.name, zhangsan.sex, zhangsan.age); struct stu lisi; lisi = zhangsan; printf("%d - %s - %c - %d\n", lisi.id, lisi.name, lisi.sex, lisi.age); return 0; }
|
输出结果:
image
结构体数组
结构体数组是个数组,由若干个相同类型的结构体变量构成的集合。
结构体数组的定义方法:
1
| struct 结构体类型名 数组名[元素个数];
|
例如:
1 2 3 4 5 6 7
| struct stu { int num; char name[20]; char sex; };
struct stu edu[3];
|
结构体数组元素的引用:
结构体数组元素对成员的使用:
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <stdio.h>
typedef struct { int num; char name[20]; float score; } STU;
int main(int argc, char *argv[]) { STU edu[3] = {{101, "Lucy", 78}, {102, "Bob", 59.5}, {103, "Tom", 85}}; for (int j = 0; j < 3; j++) { printf("%d - %s - %.2f\n", edu[j].num, edu[j].name, edu[j].score); } float sum = 0; for (int i = 0; i < 3; i++) { sum += edu[i].score; } printf("平均成绩为%.2f\n", sum / 3);
return 0; }
|
输出结果:
image
结构体指针
结构体的地址,结构体变量存放内存中,也有起始地址。
结构体指针变量的定义方法:
结构体指针变量对成员的引用:
1 2
| (*结构体指针变量名).成员 结构体指针变量名‐>成员
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <stdio.h> #include <stdlib.h> #include <string.h>
struct stu { int id; char name[32]; char sex; int age; };
int main(int argc, char *argv[]) { struct stu *s; s = (struct stu *)malloc(sizeof(struct stu)); s->id = 1001; strcpy(s->name, "张三"); s->sex = 'B'; s->age = 20; printf("%d - %s - %c - %d\n", s->id, s->name, s->sex, s->age);
return 0; }
|
输出结果:
image
结构体内存分配问题
规则 1:以多少个字节为单位开辟内存
为结构体变量分配内存时,先去找结构体的基本数据类型,找到占用字节数最大的那个类型,以它为基准开辟空间。
- 成员中只有
char
类型,以 1 字节为单位开辟空间。
- 成员中出现了
short
类型,没有更大的基本数据类型,以 2
个字节为单位开辟内存。
- 出现了
int
、float
,没有更大字节的基本数据类型,以 4
个字节为单位开辟内存。
- 出现了
double
- 在
msvc
中,以 8 个字节为单位开辟空间。
- 在
gcc
中,以 4 字节为得开辟空间。
规则 2:字节对齐
char
:1 字节对齐,即存放 char
类型的变量的地址是 1 的倍数。
short
:2 字节对齐,即存放 short
类型的变量的地址是 2 的倍数。
int
:4 字节对齐,即存放 int
类型的变量的地址是 4 的倍数。
long
:在 32 位的操作系统中,4 字节对齐,即存放
long
类型的变量的地址是 4 的倍数。
float
:4 字节对齐,即存放 float
类型的变量的地址是 4 的倍数。
double
:
- 在
msvc
中,8 字节对齐,即存放 double
类型的变量的地址是 8 的倍数。
- 在
gcc
中,4 字节对齐,即存放 double
类型的变量的地址是 4 的倍数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| struct stu { int num; int age; } lucy;
struct stu { char sex; int age; } lucy;
struct stu { char a; short int b; int c; } temp;
struct stu { char a; int c; short int b; } temp;
struct stu { char buf[10]; int a; } temp;
struct stu { char a; double b; };
|
字节对齐是一种空间换时间的操作。
位段
在结构体中,以位为单位的成员,称之为位段(位域)。
1 2 3 4 5 6 7
| struct packed_data { unsigned int a : 2; unsigned int b : 6; unsigned int c : 4; unsigned int d : 4; unsigned int i; } data;
|
image
注意:不能对位段成员取地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include <stdio.h>
struct packed_data { unsigned int a : 2; unsigned int b : 6; unsigned int c : 4; unsigned int d : 4; unsigned int i; } data;
int main() { printf("%d\n", sizeof(data)); printf("%p\n", &data); printf("%p\n", &(data.i)); return 0; }
|
输出结果:
image
注意:
- 对于位段成员赋值时不要超出位段定义的范围,例如成员 a 定义为 2
为,则去哦最大值为 3,如果给 a 赋值为 5,会取最低 2 位进行赋值。
- 位段成员必须为整型或字符型。
- 一个位段成员必须存放在一个存储单元中,不能跨两个单元,第一个单元不能容纳下一个位段,则该空间不用,从下一个单元其存放该位段。
位段的存储单元:
char
型的位段,存储单元是 1 个字节;
short int
型的位段,存储单元是 2 个字节;
int
型的位段,存储单元是 4 字节;
long int
型的位段,存储单元是 4 字节。
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <stdio.h>
struct stu { char a : 7; char b : 7; char c : 2; } temp;
int main() { printf("%d\n", sizeof(temp)); return 0; }
|
输出结果:
image
位段的长度不能大于存储单元的长度:
char
型位段不能大于 8 位;
short
型位段不能大于 16 位;
int
型位段不能大于 32 位;
long
型位段不能大于 32 位。
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <stdio.h>
struct stu { char a : 9; char b : 7; char c : 2; } temp;
int main() { printf("%d\n", sizeof(temp)); return 0; } 分析:编译出错,位段a不能大于其存储单元的大小
|
如一个段要从另一个存储单元开始,可以定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <stdio.h>
struct m_type { unsigned char a : 1; unsigned char b : 2; unsigned char : 0; unsigned char c : 3; };
int main() { struct m_type temp; printf("%d\n", sizeof(temp)); return 0; }
|
输出结果:
image
长度为 0 的位段作用是使下一个位段从下一个存储单元开始存放,将 a、b
存储在一个存储单元中,c 存储在下一个单元。
可以定义无意义位段,如:
1 2 3
| unsigned a: 1; unsigned : 2; unsigned b: 3;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| struct data { char a : 1; char b : 1; char c : 1; char d : 1; char e : 1; char f : 1; char g : 1; char h : 1; } temp;
int main() { char p0; temp.a = 1; p0 = *((char *)(&temp)); return 0; }
|
共用体
共用体和结构体类似,也是一种构造类型的数据结构。
定义共用体类型的方法和结构体非常相似,把 struct
改成
union
就可以了。
在进行某些算法的时候,需要使几种不同类型的变量存到同一段内存单元中,几个变量所使用空间相互重叠,共用体就是这样一种结构。
共用体所有成员占有同一段地址空间,共用体的大小是其占内存长度最大的成员的大小
共用体的特点:
- 同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用。
- 共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖。
- 共用体变量的地址和它的各成员的地址都是同一地址。
- 共用体变量的初始化
union data a={123};
,初始化共用体为第一个成员。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <stdio.h>
union un { int a; int b; int c; };
int main(int argc, char *argv[]) { union un myun; myun.a = 100; myun.b = 200; myun.c = 300;
printf("a = %d, b = %d, c = %d\n", myun.a, myun.b, myun.c);
return 0; }
|
输出结果:
image
枚举
将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。
枚举类型也是个构造类型的
枚举类型的定义:
在枚举值表中应列出所有可用值,也称为枚举元素。
枚举变量仅能取枚举值所列元素。
枚举变量的定义方法:
- 枚举值是常量;
- 枚举元素本身由系统定义了一个表示序号的数值,默认从 0 开始;
- 可以改变枚举值的默认值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <stdio.h>
enum week { mon = 3, tue, wed, thu, fri = 4, sat, sun };
int main(int argc, char *argv[]) {
printf("%d\n", mon); printf("%d\n", tue); printf("%d\n", wed); printf("%d\n", thu); printf("%d\n", fri); printf("%d\n", sat); printf("%d\n", sun);
return 0; }
|
输出结果:
image