目录
结构体设计
结构体使用 成员访问
结构体和数组结合
结构体大小
例题练习
结构体设计
结构体类型: 自定义数据类型 设计一个类型 int
类型的设计不占内存 用类型定义变量时才会占内存
struct 类型名{ 属性(变量的声明) };
基础使用:
struct Student{
char name[10];//成员列表(属性)
int age;
int score;
};
int main() {
struct Student stu={"qo",10,100};
struct Student*Stu=&stu;
}
指针和函数体利用typedef重命名:
struct Student{
char name[10];//成员列表(属性)
int age;
int score;
};
typedef struct Student Student;
typedef struct Student* pr;
int main() {
Student stu={"qo",10,100};
pr stu=&stu;
}
重命名的优化:
typedef struct Student{
const char* name;//成员列表(属性)
int age;
int score;
}student,*pr;
int main() {
Student stu={"qo",10,100};
pr Stu=&stu;
}
将strcut student 重命名为student,struct student *重名名为pr.(该方法既适用于.c文件,也适用于.cpp文件)
结构体使用 成员访问
1)student s={成员列表赋值}; "s .成员 " '.'为成员访问符
2) student s={赋值}; PSTU p=&s;(*p).成员 p->成员 (此处的PSTU为指向student的指针重命名)和上面的*pr相同;
举例:1)原本结构体的使用和成员访问
typedef struct Student{
char* name;
int age;
int score;
}Student;
int main() {
Student stu1={"zs",10,100};
Student stu2;
stu2={"ls",9,89};
printf("%s",stu1.name);
}
2)利用指针
typedef struct Student{
char* name;
int age;
int score;
}Student,*pr;
int main() {
Student stu1={"zs",10,100};
Student stu2;
stu2={"ls",9,89};
printf("%s",stu1.name);
pr stu_s=&stu2
printf("%s",(*stu_s).name);//注意此处'.'的优先级高于'*',所以带括号
printf("%s",stu_s->name);'->'指向符具有解引用的作用
}
结构体和数组结合
Student arr[]={{成员赋值},{成员赋值},{成员赋值}};即再Student 该结构体下有几个成员,学生1,学生2,学生3……
举例:定义结构体类型数组 赋值 打印
typedef struct Student{
const char* name;//成员列表(属性)
int age;
int score;
}student,*pr;
int main() {
Student stu = { "qo", 10, 100 };
// 定义结构体类型数组 赋值
Student arr[] = { { "af", 23, 11 }, stu };//可以用已定义的变量进行赋值
int len = sizeof(arr) / sizeof(arr[0]);//求数组长度
for (int i = 0; i < len; i++){
printf("第%d个学生: 姓名:%4s,年龄:%4d,成绩:%4d \n", i, arr[i].name, arr[i].age,
arr[i].score);
}
}
结构体大小
内存大小的基本单位:字节
1.地址访问:cpu内村读写不是按照1字节1字节的读取 以 2,4,8的倍数的字节块读取内存
2. 平台读取地址 偶数地址读取
3.不同平台 内存对齐方式不同(VS默认8 linux默认4)
结构体内存大小的计算方法:
1.变量的首地址,必须时MIN{结构体中最大基本数据类型,指定对齐方式}所占字节数的整数倍
2.每个成员变量相对结构体首地址的偏移量,都是MIN{该成员基本数据类型,指定对齐方式}整数倍
3.结构体总大小为MIN{结构体变量最大基本数据类型,指定对齐方式}所占字节的整数倍
举例:
typedef struct student{
char* name;
int age;
int score;
}student,*pr;
student str={"zs",10,100};
此处name首先占四字节(指针类型),age占4字节(int),score占四字节(int).占12个字节,我们可能看不出什么问题我们看下一段代码:
typedef struct student{
char name[10];//成员列表属性
int age;
int score;
}student,*pr;
student str={"zs",10,100};
(此处对齐方式默认8字节)数组name占10个字节,次数name的字节计算完成,接着计算age的大小4字节,然后此时结构体首地址的偏移量为10,不是这时要求变量字节和指定对齐方式最小值的的整数倍,因此向后对其两个字节(规则2),12字节,加上age的4字节,这时偏移量为16字节,16字节是接下来变量score4字节的倍数,因此不需要内存对齐,直接加上4字节,所以结果为20字节。此时20不是结构体最大基本数据类型和指定对齐方式最小值所占字节(8字节)的倍数,向后空4个字节,结果为24。
可能还不是很清楚,我们再举一例:
#include<stdio.h>
#pragma pack(4)//指定对齐方式4字节
typedef struct people{
int a;
long long b;
short c;
char d;
}people;
pragma pack()//必须存在该语句(结束)
首先计算a所占字节(4),紧接着计算b长整型(8)字节,偏移量4字节是(该变量和对齐方式最小值)4的倍数,这是偏移量为12(4+8)字节,紧接着short类型(2字节),这时 12是2(该变量和对齐方式最小值)的整数倍,所以此时直接加上2(14),14是1字节(该变量和对齐方式最小值)的整数倍,直接加上去(15),15不是结构体中最大基本类型所占字节(long long 8字节和对齐方式4字节最小值)4的倍数,因此继续对齐(16-15)1字节,(此处16是4的最小整数(4)倍)所以总共所占字节为16字节.
结构体镶嵌情况下的大小计算
struct add{
int a;
long long b;
char* c;
}
struct AD{
char* A;
int B;
struct add C;
int D;
}
计算AD结构体的大小(此处默认对齐方式8字节):首先A(char*)4字节,B为4字节,偏移量4字节是(该变量字节数4和对齐方式8字节最小值)4的整数倍,因此不需要向后空出字节,此时是8字节,然后C,此处是add结构体的字节大小,{a字节数是4不是8(下一个变量和对齐方式最小值)的整数倍,向后空出4字节,加上该变量的8字节,偏移量是16字节是4(c字节大小和对齐方式最小值)的整数倍,直接加上4字节是20字节,20不是8(最大基本数据类型和对齐方式最小值)的整数倍,向后对齐4字节,}C变量大小为24字节,8是8(对齐方式和该变量字节数最小值)的整数倍,直接加上C的字节数32,32是4(D大小和对齐方式最小值)的整数倍,因此直接相加是36字节,最后36不是8(该结构体最大基本数据类型long long 8字节和对齐方式8字节最小值)的整数倍,向后空出4字节,所以AD结构体大小为40字节。
例题练习
建立一个学生结构体,成员属性:姓名 成绩
利用冒泡排序将其按成绩升序进行排序,若成绩相同则按姓名的降序进行排序。
冒泡排序的方法参考以前的博客进行学习,此处不说明。具体讲函数体的用法
代码:
typedef struct student{//定义结构体
const char* name;
int score;
}student, *stu;//此处对结构体和指针进行重命名
void swap(stu stu_s, int i,int j){
/*交换函数,将stu_s函数的第i个成员和第j个成员进行交换,此时成员内部的属性均交换*/
student temp=stu_s[i];
stu_s[i] = stu_s[j];
stu_s[j] = temp;
}
void point(stu stu_s,int len){
int flag = 0;//标记
for (int i = 0; i < len; i++){
flag = 0;
for (int j = 0; j<len - i - 1; j++){
if (stu_s[j].score>stu_s[j+1].score) {//成绩比较大小(成员属性的调用)
swap(stu_s, j, j+1);
flag = 1;
}
if (stu_s[j].score == stu_s[j+1].score) {//成绩相同
if (strcmp(stu_s[j].name, stu_s[j+1].name)){//姓名比较大小,成员的姓名属性调用
flag = 1;swap(stu_s, j+1, j);
}
}
}
if (flag == 0) break;
}
}
上面代码用指针重命名,此处直接对结构体进行重新命名。(可用性更强)
typedef struct student{
const char* name;
int score;
}student;
typedef student ET;//对student这一结构体做一重命名
void swap(ET *i, ET*j){
ET temp = *i;
*i = *j;
*j = temp;
}
void point(ET* stu_s, int len){
int flag = 0;
for (int i = 0; i < len; i++){
flag = 0;
for (int j = 0; j<len - i - 1; j++){
if (stu_s[j].score>stu_s[j + 1].score) {
swap(&stu_s[j], &stu_s[j+1]);
flag = 1;
}
if (stu_s[j].score == stu_s[j + 1].score) {
if (strcmp(stu_s[j].name, stu_s[j + 1].name)){
flag = 1; swap(&stu_s[j + 1], &stu_s[j]);
}
}
}
if (flag == 0) break;
}
}