目录
1.结构体
1.1 结构体类型的声明
数组是一组相同类型的元素集合
结构体也是一些值的集合,只不过成员的类型可以不同
1.2 结构体变量的定义和初始化
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct book {
char bookname[50];
int price;
};
struct student {
struct book a;//结构体的嵌套
int age;
char name[20];
};
int main()
{
//初始化
struct student s = { {"c语言",45}, 20,"同学"};
//因为结构体嵌套定义,所以初始化也需要嵌套
struct student* ps=&s;
//访问方法一:
printf("%d\n", s.age);
printf("%s\n", s.name);
printf("%s\n", s.a.bookname);
//访问方法二:
printf("%d\n",ps->age);
printf("%s\n", ps->name);
printf("%s\n",ps->a.bookname);
return 0;
}
1.3 结构体的自引用
在结构体中不是包含同类型的结构体变量,而是包含同类型的结构体指针
typedef struct Node
{
int data;
struct Node* next;
}Node,*LinkList;
//Node *L=LnkList L,这俩句代码的作用都是创建一个指向该结构体的struct Node *L指针
1.4 结构体内存对齐
- 结构体的第一个成员放在结构体变量在内存中存储位置的0偏移处开始
- 从第二个成员变量往后的所有成员,都放在一个对齐数(成员的大小和默认对齐数的较小值)的整数的整数倍地址处
- 结构体的总大小是结构体所有成员的对齐数中最大的那个对齐数的倍数
- vs默认对齐数:8
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
struct s
{
int i;
char ch1;
int i2;
};
struct s1
{
int i;
char ch1;
double d;
};
int main()
{
struct s s = { 0 };
struct s1 s1 = { 0 };
printf("%d\n",sizeof(s));//12
printf("%d\n", sizeof(s1));//16
return 0;
}
对于默认对齐数的修改
#pragma pack(2)//修改默认对齐数为2
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#pragma pack(2)//修改默认对齐数为2
struct s1
{
char ch2;
int i;
char ch1;
};
int main()
{
struct s1 s1 = { 0 };
printf("%d\n", sizeof(s1));
return 0;
}
注:offsetof宏:用于计算结构体中某变量相对于首地址的偏移量
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>//offsetof宏的头文件
struct s1
{
char ch1;
int i;
char ch2;
};
int main()
{
struct s1 s1 = { 0 };
printf("%d\n", offsetof(struct s1,ch1));//0
printf("%d\n", offsetof(struct s1, i));//4
printf("%d\n", offsetof(struct s1, ch2));//8
return 0;
}
1.5 结构体传参
- 传参:函数传参的时候是需要压栈的,会有时间和空间上的系统开销,如果结构体过大,参数压栈的系统开销较大,所以导致性能下降
- 传址:不需要新开辟空间
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
struct s1
{
int data[1000];
int num;
};
struct s1 S = { {1,2,3,4,5},1000 };
//传值
void print1(struct s1 ss){
ss.num = 456;
printf("%d\n",ss.num);
}
//传址
void print2(struct s1 *p){
p->num = 123;
printf("%d\n",p->num);
}
int main()
{
print1(S);
print2(&S);
printf("%d", S.num);
return 0;
}
1.6 结构体实现位段
C语言中的位段:位段的声明和结构体是类似的,有俩个不同
- 位段的成员必须是int,unsigned int 或 signed in
- 位段的成员名后有一个冒号和一个数字
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
struct A
{
int _a : 2;//_a成员占2个bit位
int _b : 5;//_b成员占5个bit位
int _c : 10;//_c成员占10个bit位
//剩15比特位不够,在开辟32个比特位
//注:位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免位段
int _d : 30;//_d成员占30个bit位
};
struct S
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
};
int main()
{
//写出内存中16进制存储的数据
struct S s = { 0 };
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
return 0;
}
2.枚举
枚举顾名思义就是一一列举
把可能的取值一一列举
2.1 枚举类型的定义
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#define Red 5
#define Green 5
#define Blue 5
enum color {
red=5,
green,
blue
};
int main()
{
printf("%d\n",red);//5
printf("%d\n", green);//6
printf("%d\n", blue);//7
return 0;
}
2.2 枚举的优点
枚举的作用于#define定义常量有几乎相同的效果,那么用枚举的优势在哪?
- 增加代码的可读性和可维护性
- 和#define定义标识符比较枚举有类型检查,更加严谨
- 防止了命名污染(#define定义的常量,任何地方都可以使用,枚举可以使用局部定义)
- 便于调试
- 使用方便,一次可以定义多个常量
2.3 枚举的使用
例如:一个菜单栏,用枚举类型写有更高的可读性
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
menu()
{
printf("--------------------------------------\n");
printf("------------1.add 2.sub------------\n");
printf("------------3.mul 4.div------------\n");
printf("----------------0.exit----------------\n");
printf("--------------------------------------\n");
}
enum option
{
EXIT,//0
ADD,//1
SUB,//2
MUL,//3
DIV//4
};
int main() {
int input = 0;
do
{
menu();
printf("请选择操作");
scanf("%d", &input);
switch (input)
{
case ADD:
printf("add\n");
break;
case SUB:
printf("sub\n");
break;
case MUL:
printf("mul\n");
break;
case DIV:
printf("div\n");
break;
case EXIT:
printf("exit\n");
break;
}
} while (input);
}
3.联合
联合体这种类型定义的变量也包含一系列的成员,特征是这些成员共用同一块空间(所以联合也叫共用体)
3.1 联合类型的定义
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
union un
{
char c;
int i;
};
int main()
{
union un u;
//printf("%d", sizeof(u));//结果:4
printf("%p\n",&u);
printf("%p\n", &(u.c));
printf("%p\n", &(u.i));
//u、u.c 、u.i的地址相同
return 0;
}
3.2 联合体的特点
联合体的大小可能是成员变量中最大的哪一个的大小,也可能不是,因为在联合体中也存在内存对齐
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
union u
{
char a;
int i;
};
union un
{
char arr[5];
int i;
};
int main()
{
printf("%d\n", sizeof(union u));//运行结果:4
printf("%d\n",sizeof(union un));//运行结果:8
//内存进行了对齐,在五个字节后,i变量需要到4的倍数
return 0;
}
3.3 联合体的应用
题目:查看电脑是大端字节序存储还是小端字节序存储(这是采用联合体进行查看,关于大小端字节序的介绍和其他算法在这数据在内存中的存储)
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
union un
{
int i;
char c;
};
int main()
{
union un u = { 0 };
u.i = 1;
if (u.c == 1)
{
printf("小端字节序");
}
else
{
printf("大端字节序");
}
return 0;
}