一,前言
以下是我在学习结构体过程中对结构的相关总结。
二,结构体简述
1.结构体
具有相同或不同类型元素的集合叫做结构体。结构体本质是一种类型。结构体通常用关键字 struct 定义。如定义一个学生类型结构体。
struct Student
{
char name[50];
int Chinese;
int Math;
int English;
};
2.结构体全局变量声明
//此代码主要用于说明如何重命名定义
struct Student
{
char name[50];
int Chinese;
int Math;
int English;
};
//全局变量声明 ,用typedef实现
//如typedef int char 此时的char不再是基本数据类型char,而是代表着int类型
//在如:typedef unsigned long long unit64
// ① unsigned long long val=0;
// ②uniit64 val=0; ①与②所代表的相同
此时可以将typedef与结构体定义结合,实现重命名,简化后续代码,如上述的学生类型结构体
typedef struct Student
{
char name[50];
int Chinese;
int Math;
int English;
}Student;
此后可以用Student代替struct Student.
3.结构体赋值
以下为结构体赋值
#include <string.h>//此头文件为strcpy 与 memcpy 与strncpy的头文件
#include <stdio.h>
struct Student
{
char name[50];
int age;
};
int main()
{
struct Student s = { "yxb", 18 };//初始化变量
strcpy(s.name, "ls");//对变量进行赋值改变 strcpy为关键字拷贝字符串
memcpy(s.name, "ls", 3);//也是拷贝关键字,意义为将ls中的3个字符赋值给s.name
//注意memcpy中要赋值的3个字符包括'\0'
strncpy(s.name, "ls", 2);//拷贝关键字, 意义为将ls中的个字符赋值给s.name
//注意strncpy中要赋值的2个字符不包括'\0'
s.age = 24;
return 0;
}
4.结构体访问
#define _CRT_SECURE_NO_WARNINGS
#include <string.h>
#include <stdio.h>
typedef struct Student
{
char name[50];
int age;
}stu;
int main()
{
// 通过'.'访问结构体成员
stu s;
strcpy(s.name, "yxb");
s.age = 20;
// 通过指针'->'访问结构体成员
stu * p = &s;
printf("%s\n", p->name);
printf("%d\n", p->age);
//通过不同的方式访问
printf("%s\n", (*p).name);
printf("%d\n", (*p).age);
return 0;
}
打印结果:
在函数传参时,会生成临时变量,若采用传值方式,传输的结构体较大则占用空间大,顾建议采用传值方式。
5.结构体数组
结构体数组:是指数组中的每一个元素都是结构体。结构体数组在定义的同时也可以初始化,同时可以定义不同的数值。
typedef struct stu
{
char name[20]; //姓名
int num; //号码
int age; //年龄
}stu;
int main()
{
stu s[2] =
{
{"zs", 18,14},
{"yxb", 4,13},
};
return 0;
}
6.结构体简述代码示例
银行卡类型:卡号,密码,钱,银行名,持卡人{姓名,身份证号,电话号码},是否被锁,记录[10]{时间,事件,金额} 写出结构体
//以下代码仅供参考,具体细节不在补充
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
typedef struct BankCard
{
int _id;
int _passwd;
float _money;
char _bankname[10];
bool _islocked;
User user;
Record record[10];
}BankCard;
typedef struct User
{
char _name[10];
char _idcord[20];
char iphhone[10];
}User;
typedef struct Record
{
char _time[50];
char _typr[10];
float _rmoney[10];
}Record;
int main()
{
BankCard card = { 123,123,0.0,"建行",false,{"zs","610481200312270019","110"},{"2024....","存款",200.0}};
return 0;
}
若要改变结构体内已经赋值的数如:改变记录里面的金额
card.record[1]._rmoney=-800;
三, 结构体大小的计算
1.单个结构体计算方法
struct A
{
char c;//1+3
int a;//4
short s;//2
};
sizeof(A);10+2=12B
下面我将对上述代码进行解释:
已知char类型占1个字节,int类型占据4个字节,由于内存对齐,前方偏移量之和要为后方的整数倍,顾对char类型原有1字节加上3,进行内存填充(后面会说到内存填充),相同的道理,Short类型为2个字节,前方偏移量之和为8,是2的整数倍,故不用进行内存填充。
当计算结构体整体大小时,将算上的所有字节大小相加,相加之和若为结构体内最大字节数的整数倍,则得出来的值为结构体的大小,若相加之和不为结构体内最大字节的整数倍,则要进行内存填充,将其扩大到最大字节的整数倍。
2.多个结构体计算方法
struct A
{
char c;//1+1
short s;//2
};//4B
struct B
{
char c;//1+1
A a;//4
short s;//2
};
sizeof(B)----->8B
下面我将对上述代码进行解释:
相信结构体A已经没有什么问题了,估计问题主要集中在结构体B中为什么char c 内存填充只加1。
因为在多个结构体计算时,若遇到像结构体B里面的情况,char c 的内存偏移量只算结构体A里面的最大字节及结构体A里面的short,占据两个字节,故内存填充值只加1。
3.指定对齐方式
VS 中的默认对齐数为 8,不是所有编译器都有默认对齐数,当编译器没有默认对齐数的时候,成员变量的大小就是该成员的对齐数。
要是需要修改默认对齐方式则用:#pragam pack() ------->括号里面填写需要设置的默认对齐值
#pragma pack(2)
struct A
{
char c;//1+1
short s;//2--->min{short,2}
int a;//4----->min{int ,2}
};//8B---->min{4,2}
下面我将对上述代码进行解释:
将VS原有的默认对齐值8改为对齐值为2,及对结构体A里面的每一个不同的部分,将他们本身的字节大小与2进行对比,只需要选最小的对比,若当前字节为最小的数的整数倍即可,若不为整数倍进行内存填充。
4.内存对齐
1)内存大小的基本单位是字节(byte),理论上来讲,可以从任意地址访问变量,但是实际上,cup并非读写内存,而是以2,4,或8的倍数的字节块来读写内存,因此就会对基本数据类型的地址作出一些限制,即它须是 2,4或8的倍数。那么就要求各种数据类型按照一定的规则在空间上排列,这就是对齐。
2)有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结身字节进行拼凑才能得到该32bit数据。显然在读取效率上下降很多。
3)由于不同平台对齐方式可能不同,如此一来,同样的结构在不同的平台其大小可能不同,在无意识的情互相发送的数据可能出现错乱,甚至引发严重的问题。
5.位段设计
#include <stdio.h>
typedef struct room
{
unsigned short _homeid : 10;//2个字节中的10个位
unsigned short _num : 3;//2个字节中的3个位
unsigned short _isused : 1;//2个字节中的1个位
}rom;//一共占据10+3+1=14bit --->2B
int main()
{
printf("%d",sizeof(rom));
return 0;
}
运行结果:
四,小代码练习
设计学生结构体{姓名,语文成绩,数学成绩,英语成绩}
//5个学生
//1.求语文平均成绩
//2.英语成绩最高分?获取最高分学生信息
#include <stdio.h>
typedef struct Student
{
char name[50];
int Chinese;
int Math;
int English;
}Student;
int main()
{
Student student[5] = { { "yang",40,50,30 }, { "zhao",30,40,50 }, { "sun",30,40,50 }, { "qian",30,40,50 },{"zhang",30,40,80} };
int i=0;
int allchinese = 0;
for (i; i < 5; i++)
{
allchinese += student[i].Chinese;
}
int AVGchinese = allchinese /5 ;
printf("语文平均:%d\n", AVGchinese);
int j = 1;
int maxenglish = student[0].English;
Student message = student[0];
for (j; j < 5; j++)
{
if (student[j].English > maxenglish)
{
maxenglish = student[j].English;
message = student[j];
}
}
printf("英语最高:%d\n", maxenglish);
printf("姓名:%s\n英语成绩:%d\n数学成绩:%d\n语文成绩:%d\n", message.name,message.English, message.Math, message.Chinese);
return 0;
}
结果:
也可以使用函数书写,这里不再过多叙述。