结构体:struct,用户自己设计的 不同类型组合的 组合数据类型
格式
调用结构体
结构体的打印
指针与结构体的结合
引用与结构体结合
结构体的套接
结构体:内存对齐
共用体:union,共用内存,达到对不同的类型进行解析的目的
共用体的用法
//quesiton:如何实现CSDN博客间的跳转
格式:
struct Student{
char name[20]; //姓名
int num[20]; //学号
int age; //年龄
char group; //所在学习小组
float score; //成绩
};
int main(){
Student student; //定义结构体变量
student.s_age=22;//给结构体内部赋值
student.s_name[20]="xiaoxuxu"; //×
//结构体内若是一个数组或者字符串,不能直接赋值
//需要用拷贝函数
strcpy(s1.s_name,"string");
//可以整体打印? 答案:不可以
//除了整体初始化外,其余的都是分开操作,一个一个打印出来
}
访问并修改结构体内部数据:
1.逐个赋值: 使用 . (成员访问符)
2.整体赋值: Student s1 = {"LU","201901",18,"A",80}; // 利用花括号整体一一对应初始化
//注意:结构体内部为int num[20]; 赋值的时候是201901
//当结构体内部为char num[20]; 赋值的时候是"201901"
//注意:初始化时,不能跳着初始化(略过其中属性)
Student s2 = s1; //整体赋值 本质是调用内存拷贝函数 my_memmove(&s3,&s1,sizeof(Student));
//值传递:直接开辟了一个Student类用来打印
void Print_Student(Student stu){printf: stu.s_name , stu.s_num....}
main:Print_Student1(stu1);
//指针:指针只开辟4个字节,相对节省空间,
//缺点:为了防止不小心改变pstud的指向的内容 需要在前面加const 防止指向的内容被修改
//缺点:使用指针 一定要判空,防止空指针的存在
void Print_Student1(Student const *pstud)
{
if(NULL == pstud) return ;
printf: pstud->s_name , pstud->s_num....
}
main:Print_Student2(&stu1); //指针传递的是地址
//引用(起小名):避免开辟空间造成的空间浪费
void Print_Student1(Student const &stu){printf: stu.s_name , stu.s_num....}
main:Print_Student1(stu1);
指针与结构体的结合:(指向作用 -> )
ps ->s_age = 10; //ps指针指向s1
(*ps).s_age = 10; //等价
//.和*的优先级 => .的优先级first
//引用与结构体的结合:
Student &s = s2;//定义了一个Student类的变量s2,给s2起小名叫s
结构体可以套接结构体,但是函数不可以套接函数
(函数内部可以申明函数,不可以新定义一个函数)
//定义函数 //×
int Add(int x int y){
return x+y;
}
//申明函数 //√
int Add(int x int y);
int fun(int ,int );
//结构体套接结构体:
struct Date
{
int year;
int month;
};
struct Student
{
char s_id[20];
struct Date birthday; //定义一个Date型的结构体变量 birthday
};
//=========等价=========
struct Student
{
char s_id[20];
struct Date
{
int year;
int month;
};
struct Date birthday; //struct 在.cpp文件中可以省略
};
结构体:内存对齐(存疑)
内存对齐遵循原则:
1.当前成员所在的地址 能够整除 成员本身的字节大小 (不包括数组)
2.结构体的整体大小能够能整除除成员最大基本类型 大小(不包括数组)
3.从零地址开始另一种理解:
偏移量:结构体中的偏移量就是结构体成员和结构体变量的地址之差
计算结构体大小的规则:
1.每一个成员的偏移量都必须是该成员的倍数。
2.结构体的大小必须是该结构体字节数最大成员的倍数。
例如下面的结构体:
struct A
{
char a;
short b;
int c;
};
/*第一个成员的偏移量都是0;一般可以不看,a的大小为1,所以成员b的偏移量为1,b的大小
为2,1不是2的倍数,所以应该将1+1,成为2的倍数,成员c的偏移量就为1+1+2,是成员c的
倍数,现在就符合计算结构体大小的第一条:改成员的偏移量是该成员大小
的倍数,还有第二条规则:结构体大小是结构体最大成员的倍数,结构体的大小就是各个成员
之和,a;2,b:2,c:4加起来就是8,该结构最大成员为c大小为4,满足第二个条件,所以该结
构体的倍数就是8*/
参考博客:https://www.cnblogs.com/smile-812/p/7897187.html
struct AA
{
char a; // 1 + 3
short b; // 4
int c; // 4
double d; // 8
}; //16
struct A
{
char a; // 1 + 3 [0,1,2,3] (1,2,3浪费)
int b; // 4 [4,5,6,7]
}; //[0-7] 大小为8,向前对齐
struct A1
{
char a; //1
char c; //1
int b; //4
}s; // 大小为8
struct B
{
char a; //1 + 1 [0,1]
short b; //2 [2,3]
int c; // 4 [4,5,6,7]
}; // [0-7]大小为8,向前对齐
struct C
{
char a; // 1 + 3 [0,1,2,3]
int b; // 4 [4,5,6,7]
short c; // 2 + 6 [8-15]
double d; // 8 [16-23]
}; // [0-23]大小为24,向前对齐
struct D
{
char a; // 1 [0]
char b; // 1 [1]
short c; // 2 [2,3]
int d; // 4 [4,5,6,7]
}; // [0-7]大小为8,向前对齐
struct E
{
char a; // 1 + 1 [0,1]
short c; // 2 [2,3]
char b; // 1 + 3 [4,5,6,7]
int d; // 4 [8,9,10,11]
}; // [0-11]大小为12,向前对齐
struct F
{
int a; // 4 [0,1,2,3]
char c; // 1 + 3 [4,5,6,7]
}; //[0-7]大小为8,向后对齐,一般只有两个数据成员
struct G
{
char ch[17]; //17 + 3 20能被4整除
int i; // 4
float f; //4
}su; // 大小为28
struct H
{
char a; //1 +1
short b; //2
char c; //1 +3
int d; //4
char e[3]; //3 +1
}; // 大小为16
干预内存对齐:
#pragma pack(n) // n = 1,2,4,8,16
//n = 1;强制要求内存按照1字节对齐;
共用体:
共用体:共用内存 ;; 最主要的用途 : 对不同的类型进行解析
结构体:自己独占内存,所占内存是各成员占用内存长度之和(存在内存对齐)
union A{
char a;
short b;
int c;
};
struct B{
char a;
short b;
int c;
};
int main(){
A x1;
//x1.a = 'a'; //这里内存互相侵占了
//x1.b = 12;
B x2;
x2.a = 'a'; //自己用自己的 不影响
x2.b = 12;
}
共用体的用法:对同一个空间按不同类型去识别
//共用体可以套用结构体
//结构体可以套用联合体
//直接使用共用体
union Node{
unsigned int addr;
unsigned char s1,s2,s3,s4;
//对于联合体而言s1,s2,s3,s4都用的是同一个字节
};
//共用体套用结构体
union Node{
unsigned int addr;
sruct{
unsigned char s1,s2,s3,s4; //分布于四个字节了
};
}
//结构体套用共用体
struct Node{
char ch;
union{ //无名共用体:哑元结构
int a; //a和f共享同一个内存
float f;
};//1+4 =6 凑/4 =>8个字节
};
//这里还需深一步理解,为何要这么做! 每种结构都有什么好处