1.结构体
(1)内容
定义:
1.用户自定义的数据类型
2.可以包含若干不同数据类型(可相同)的成员变量
3.这些数据项组合起来反应某一信息
格式:
struct 结构体名 (用户自定义的数据类型)
{
数据类型 成员变量1;
数据类型 成员变量2;
数据类型 成员变量3;
...
};
(2)结构体变量
(结构体类型定义的变量)
格式:
struct 结构体 变量名
定义方式:
①先定义结构体类型,再定义其变量
struct 结构体名
{
成员变量;
};
struct 结构体名 变量名;
②定义结构体同时,定义结构体变量
③缺省结构体名定义结构体变量
(3)赋值
① 定义变量的同时直接大括号赋值 (初始化)
②定义变量时未初始化,然后对成员变量单独赋值
③点等法赋值
(4)访问
通过 .访问:结构体变量名.成员变量名
scanf("%s %d %d %d", stu.name, &stu.id, &stu.score, &stu.age);
printf("%s %d %d %d\n", stu.name, stu.id, stu.score, stu.age);
(5)重定义 typedef
对变量进行重命名
typedef int int _num;
int == int_num
int a; == int_num a;
对指针地址进行重命名
typedef int * int _p
int * == int_p
int *p = &a; == int _p p = &a;
① 定义结构体的同时重定义
typedef struct student
{
int id;
int score;
} STU; // STU是结构体类型重定义的名字
struct student stu; == STU stu;
② 先定义结构体,然后重定义
struct student
{
int id;
int age;
};
typedef struct student STU;
2.结构体数组
(结构体相类型变量组成的数组)
(1)格式
① 定义结构体的同时定义结构体数组
②先定义结构体,然后定义结构体数组
struct student
{char name[32];int id;int score;int age;
};struct student stu[3];
(2)赋值:
① 定义结构体数组的同时赋值
②先定义结构体数组。再对结构体数组的每一个人元素分别赋值
(3)结构体数组的输入输出:(for循环遍历)
#include <stdio.h>
#include <string.h>struct student
{char name[32];int id;int score;int age;
};int main(int argc, char const *argv[])
{struct student stu[3];for(int i = 0; i < 3; i++){scanf() \ printf();}return 0;
}
(4)结构体数组的大小
sizeof(结构体数组名);
元素个数 * 结构体类型大小
3.结构体指针
(指向结构体的指针)
(1)格式:
存储类型 数据类型 *指针变量名
struct 结构体名 *结构体指针变量名
(2)赋值:
格式:结构体指针变量 -> 成员变量
-> 指向的
#include <stdio.h>
#include <string.h>struct student
{int id;int age;
} stu;struct work
{int id;int score;
} w;int main(int argc, char const *argv[])
{struct student *p1 = &stu;
p1->id = 1;
p1->age = 18;printf("%d %d\n", stu.id, stu.age);return 0;
}
(*p1).成员变量
练习:
创建一个结构体数组,数组名为student,成员包含学号,姓名,成绩(数据类型自己设定)从终端输入学生信息,封装函数实现按成绩从低到高打印学生信息。
#include <stdio.h>
#include <string.h>#define N 3struct student
{int id;char name[32];int score;
};void fun(struct student *p)
{struct student temp;for(int i = 0; i < N-1; i++){for(int j = 0; j < N-1-i; j++){if((p+j)->score > (p+j+1)->score){
temp = *(p+j);*(p+j) = *(p+j+1);*(p+j+1) = temp;}// if(p[j].score > p[j+1].score)// {// temp = p[j];// p[j] = p[j+1];// p[j+1] = temp;// }}}for(int i = 0; i < N; i++)//printf("%d %s %d\n", p[i].id, p[i].name, p[i].score);printf("%d %s %d\n", (p+i)->id, (p+i)->name, (p+i)->score);
}int main(int argc, char const *argv[])
{struct student stu[N];for (int i = 0; i < N; i++)scanf("%d %s %d", &stu[i].id, stu[i].name, &stu[i].score);fun(stu);return 0;
}
(2)结构体大小
sizeof(struct 结构体名) // 结构体类型大小
struct student
{char a;short w;char y;int b;char c;
} stu;
结构体大小遵循字节对齐原则
1.字节对齐:
在实际使用中,访问特定数据类型变量时需要在特定的内存起始地址进行访问,这就需要各种数据类型按照一定的规则在空间上进行排列,而不是顺序地一个接一个地存放,这就是字节对齐
2.字节对齐原则:
- 在64位系统下默认的value值为8字节,判断结构体类型中最大成员变量的字节大小,和默认的value值进行比较,按小的数进行对齐
- 结构体成员进行对齐时遵循地址偏移量是成员类型的整数倍
- 结构体成员按顺序进行存储,如果不满足以上条件时,需要填充空字节
3.进行字节对齐的原因:
- 平台原因:不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。(提高程序的移植性)
- 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
- 内存原因:在设计结构体时,通过对齐规则尽可能优化结构体的空间大小至最小空间
例子:
struct student
{char a;short w;char y;int b;char c;
} stu; // 大小 16
第一步:找出成员变量的大小,让其与编译器的默认的对齐数进行比较,取较小的值作为该成员变量的对齐数
第二步:根据每个成员对应的对齐数画出他们在内存中的相对位置
第三步:计算结构体大小
4.枚举
维基百科的理解:枚举类型用于声明一组命名的常数,当一个变量有几种可能的取值时,可以将它定义为枚举类型。 定义:是指将变量的值一一列出来,变量的值只限于列举出来的值的范围内。
我的理解:枚举类型就是将一些比较固定的值一一列举出来,枚举类型和宏定义是差不多的,只有细微区别,宏运行是在预处理阶段完成的,枚举类型是在与编译阶段完成的。
定义:
用户自定义数据类型,用于声明一组常数
格式:
enum 枚举名
{
value1,
value2,
value3
...
};
未赋初值时,第一个常数默认是0,后续的常数依次加一(如果第一个值赋值为1,从赋值的1开始递增)
#include <stdio.h>
#include <string.h>
enum week
{
MON = 1,
TUE,
WED,
Thurs,
Fri
};
int main(int argc, char const *argv[])
{int day;scanf("%d", &day);switch (day){case MON: printf("Monday"); break;case TUE:printf("Tuesday"); break;case WED:printf("Wednesday"); break;case Thurs:printf("Thursday"); break;case Fri:printf("Friday"); break;default:break;}return 0;
}
5.共用体
不同类型的成员变量共用同一块地址空间
格式:
union 共用体名
{
成员列表;
}
(1)定义共用体变量
union 共用体名 变量名;
#include <stdio.h>
#include <string.h>union val
{int a;char ch;
} v;int main(int argc, char const *argv[])
{
v.a = 10;// 他俩不能同时出现,是以最后一次赋值为准
v.ch = 'a';printf("%d\n", v.a);return 0;
}
(2)特性:
- 共用体成员共用同一块地址空间
- 赋值顺序以最后一次为准
- 共用体大小为成员中类型最大的数据的大小
(3)使用共用体测试大小端
#include <stdio.h>
#include <string.h>union val
{int num;char ch;
} v;int main(int argc, char const *argv[])
{
v.num = 0x12345678;if(v.ch == 0x78){printf("小端\n");}else{printf("大端\n");}return 0;
}