1 结构体的声明
1.1 结构体基础知识介绍
结构体是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。我们知道生活中事物属性的描述都可以不同类型的变量来表示,那么我们如何表示一个复杂对象呢,比如一名学生(包括名字,年龄,性别,学号),一本书(包括书名,作者,价格),这个时候就需要结构体了。
1.2 结构体的声明
语法规则
struct tag
{
member-list;
}variable-list;
注意分号一定不能为忘
例如描述一个学生:
typedef struct Stu//struct Stu是结构体类型,typedef是将结构体类型重命名为Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}Stu;//分号不能丢
1.3 结构体成员的类型
结构的成员可以是标量、数组、指针,
甚至是其他结构体。
1.4 结构体变量的定义和初始化
struct Point
{
int x;
int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//初始化:定义变量的同时赋初值。
struct Point p3 = {x, y};
struct Stu //类型声明
{
char name[15];//名字
int age; //年龄
};
struct Stu s = {"zhangsan", 20};//初始化
struct Node
{
int data;
struct Point p;
struct Node* next;
}n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化
2 结构体成员的访问
- 结构体变量访问成员
结构变量的成员是通过点操作符(.)访问的。点操作符接受两个操作数。
举个栗子:
struct Stu
{
char name[20];
int age;
};
struct Stu s;
我们可以看到 s 有成员 name 和 age ,我们可以这样访问结构体成员
struct S s;
strcpy(s.name, "zhangsan");//使用.访问name成员
s.age = 20;//使用.访问age成员
- 结构体指针访问指向变量的成员
有时候我们得到的不是一个结构体变量,而是指向一个结构体的指针。那我们如何访问成员。
struct Stu
{
char name[20];
int age;
};
void print(struct Stu* ps)
{
printf("name = %s age = %d\n", (*ps).name, (*ps).age);
//使用结构体指针访问指向对象的成员
printf("name = %s age = %d\n", ps->name, ps->age);
}
int main()
{
struct Stu s = { "zhangsan", 20 };
print(&s);//结构体地址传参
return 0;
}
可以看到
通过结构体指针访问成员变量时我们用的是->操作符
3 结构体传参
我们直接通过代码来看
struct S
{
int data[1000];
int num;
};
struct S s = { {1,2,3,4}, 1000 };
//结构体传参
void print1(struct S s)
{
printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
printf("%d\n", ps->num);
}
int main()
{
print1(s); //传结构体
print2(&s); //传地址
return 0;
}
我们可以看到结构体传参跟普通变量一样也分为值传递和址传递,平时我们进行结构体传参的时候应该优先考虑址传递。
原因:
函数传参的时候,参数是需要压栈的。如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。
总结:
结构体传参的时候优先传结构体的地址。
4 结构体空间大小计算
小知识:
定义结构体类型,只能说明该类型的组成情况,并没有分配内存空间。只有当定义属于结构体类型的变量时,系统才会分配空间给该变量。
结构体中只有基本类型的字节大小计算:
结构体的大小计算遵循的2个原则:
1:整体空间是占用空间最大的成员(的类型)所占字节数的整数倍
,但是在32位Linix + gcc环境下,若最大成员类型所占字节数超过4,如double是8,则整体空间是4的倍数即可。
2:数据对齐原则。内存按结构体成员的先后顺序排列,当排到该成员变量时,其前面已摆放的空间大小必须是该成员类型大小的整数倍,如果不够则补齐,依次向后类推。
但在Linux + gcc环境下,某成员类型所占字节数超过4,如double是8,则前面已摆放的空间大小是4的整数倍即可,不够则补齐。
💫举个栗子:
struct str
{
char a;
double b;
int c;
char d;
};
int main()
{
struct str s;
int sz = sizeof(s);
printf("%d\n", sz);
return 0;
}
结果:
为什么是24个字节呢,我们根据前面的两个原则来看看,首先char类型为1个字节,double类型为8个字节,但是
根据原则2,double类型前面的空间必须为8的倍数
,所以char类型后面会补上7个字节从而构成8的倍数,同理,由于int类型前面的空间已经为4的倍数,所以直接放进去就行,同样后面char类型也是直接放进结构体空间里面,至此所有的成员变量都放到了结构体空间里面,此时所占空间为21个字节,最后根据原则1,结构体整体空间要为最大成员类型(double)8个字节的倍数
,所以结构体空间还会补上3个字节的空间最终构成24个字节。
结构体中包含结构体的空间大小计算:
当结构体中含有结构体时,结构体大小计算的原则为:
1:整体空间是子结构体与父结构体中占用空间最大的成员(的类型)所占字节数的整数倍,但是在32位Linix + gcc环境下,若最大成员类型所占字节数超过4,如double是8,则整体空间是4的倍数即可。
2:数据对齐原则。父结构体内存按结构体成员的先后顺序排序,当排到子结构体成员时,其前面已摆放的空间大小必须是该子结构体成员中最大类型带大小的整数倍,如果不够则补齐
,依次向后类推。但在Linux + gcc环境下,某成员类型所占字节数超过4,如double是8,则前面已摆放的空间大小是4的整数倍即可,不够则补齐。
💫举个栗子:
struct str1
{
char a;
int c;
};
struct str2
{
char a;
struct str1 b;
char c;
};
int main()
{
struct str2 s;
int sz = sizeof(s);
printf("%d\n", sz);
return 0;
}
结果:
根据前面的栗子我们很容易就可以算出struct str1的类型大小为8个字节,在struct str2中char类型为一个字节,struct str1为8个字节,根据原则2,因为struct str1中最大类型大小为int(4个字节),所以前面的char类型会补3个字节构成4的倍数,后面的char类型满足原则2,此时struct str2的大小为13个字节,根据原则1,最后结构体大小必须为4的倍数,所以会在后面补3个字节构成16个字节。
结构体中包含数组的空间大小计算:
结构体包含数组跟前面只有基本类型的计算方法类似,只不过
数组是按照单个变量一个一个进行摆放的,不是视为整体。
💫举个栗子:
struct str1
{
char c;
char a[8];
};
int main()
{
struct str1 s;
int sz = sizeof(s);
printf("%d\n", sz);
return 0;
}
结果:
作者水平有限,如果有任何错误欢迎指正,希望和大家一起学习进步!!!
❤️谢谢大家。