一.typedef struct 与 struct
C编译器中
- typedef struct
typedef struct student
{
int ID;
char name[20];
char sex;
int age;
}Stu;
这样定义结构体,可以这样初始化
Stu s1={1,"xiaoming",'m',18};
- struct
struct student
{
int ID;
char name[20];
char sex;
int age;
};
初始化如下
struct student s1={1,"xiaoming",'m',18};
二.同类型结构体赋值
typedef struct student
{
int ID;
char name[20];
char sex;
int age;
}Stu;
int main()
{
Stu s1={1,"xiaoming",'m',18};
Stu s2=s1;
}
若只给结构体中某几个变量赋值
typedef struct student
{
int ID;
char name[20];
char sex;
int age;
}Stu;
int main()
{
Stu s1={.name="xiaoming",.age=18};
Stu s2=s1;
}
未初始化的char型与char型数组输出为空,未初始化的int型输出为0
三.结构体按值传递,按地址传递
1.访问元素
-
访问结构体普通变量中各项值时,用“.”
void my_printf(Stu s)
{
printf("%d,%s,%c,%d\n",s.ID,s.name,s.sex,s.age);
}
- 访问结构体中指针变量,用“->”
void my2_printf(Stu *s)
{
(*s).ID=5;
printf("%d,%s,%c,%d\n",s->ID,s->name,s->sex,s->age);
}
2.按值传递,按地址传递
#include <stdio.h>
typedef struct birthday
{
int year;
int month;
int day;
}Bir;
typedef struct student
{
int ID;
char name[20];
char sex;
int age;
struct birthday mbirthday;
struct student *p;
}Stu;
void my_printf(Stu s)
{
printf("%d,%s,%c,%d,%d-%d-%d\n",s.ID,s.name,s.sex,s.age,(s.mbirthday).year,(s.mbirthday).month,(s.mbirthday).day);
s.ID=4;
}
void my0_printf(Stu s)
{
printf("%d,%s,%c,%d,%d-%d-%d\n",s.ID,s.name,s.sex,s.age,(s.mbirthday).year,(s.mbirthday).month,(s.mbirthday).day);
}
void my2_printf(Stu *s)
{
printf("%d,%s,%c,%d,%d-%d-%d\n",s->ID,s->name,s->sex,s->age,(s->mbirthday).year,(s->mbirthday).month,(s->mbirthday).day);
s->ID=5;
}
void my20_printf(Stu *s)
{
printf("%d,%s,%c,%d,%d-%d-%d\n",s->ID,s->name,s->sex,s->age,(s->mbirthday).year,(s->mbirthday).month,(s->mbirthday).day);
}
int main()
{
Stu s1={1,"xiaoming",'m',18,{2000,1,1}};
my_printf(s1);
my0_printf(s1);
my2_printf(&s1);
my20_printf(&s1);
my_printf(s1);
my2_printf(&s1);
return 0;
}
- 设计了4个输出函数,my_printf是按值传递的函数,先输出初始化后的s1,输出后将ID改为5
- my0_printf是再次输出s1,由于前两个输出函数是按值传递,只是副本中的ID被修改,并不是真正的s1中ID被修改,且my_printf执行完后立即释放,副本也被释放,真正的ID仍为 1,所以再次输出s1,ID仍为5(此时输出的4也为副本中的4)
- my2_printf是按地址传递的函数,此函数也是先输出s1,再将ID改为5
- my20_printf验证了按地址传递是否修改的是真正的s1中ID,实验结果表明,修改ID是修改真正s1中的ID,并不是副本
- 再次调用按值传递的my_printf与按地址传递的my2_printf,实验结果可以证明真正的s1的ID被改为5
内存示意图
按值传递
按地址传递
四.结构体内存空洞
1. 结构体内存空洞:指的是为提高效率,系统在内存中使数据存储空间“对齐”的现象
#include <stdio.h>
struct s1
{
int a;
char b;
int c;
};
int main()
{
struct s1 s;
printf("%d\n",sizeof(s));
return 0;
}
此结构体中变量类型是int,char,int,按理说应该大小为9,但由于结构体内存空洞,给int分配4个字节的空间,char类型数据只需一个字节空间,但为了对齐,也分配4个字节,只占用1个字节,剩余3个字节空闲,最后的int类型分配4个字节空间,3*4=12,整个结构体占用12个字节空间
2.
#include <stdio.h>
struct s1
{
char a;
char b;
int c;
};
int main()
{
struct s1 s;
printf("%d\n",sizeof(s));
return 0;
}
在这个结构体中,变量类型为char,char,int,最开始给char分配了一个字节空间,之后的char类型的一个字节空间紧随之前的char,为与int型变量对齐,两个char型变量共占用4个字节空间,其中2个字节使用,2个字节空闲,2*4=8,整个结构体占用8个字节空间
3.
#include <stdio.h>
struct s1
{
char a;
char b;
char c;
};
int main()
{
struct s1 s;
printf("%d\n",sizeof(s));
return 0;
}
由于此结构体有3个char型变量,每个char型变量分配1个字节空间,3*1=3,此结构体共占用3个字节空间
4.
#include <stdio.h>
struct s1
{
short a;
char b;
short c;
};
int main()
{
struct s1 s;
printf("%d\n",sizeof(s));
return 0;
}
short类型变量占用2个字节,char型变量为对齐,也被分配2个字节,使用一个,空闲一个,最后的short型占用两个字节,2*3=6,整个结构体占用6个字节空间
5.
#include <stdio.h>
struct s1
{
double a;
char b;
short c;
};
int main()
{
struct s1 s;
printf("%d\n",sizeof(s));
return 0;
}
double型变量占用8个字节空间,由于32位系统,内存以4个字节为1个单位存放数据,8=4+4,short型与char型共占用4个字节空间,3个字节使用,1个字节空闲,4*3=12,整个结构体占用6个字节空间。
6.总结
-
32位系统,内存以4个字节为1个单位存放数据
-
4个字节为一组成为字对齐,2个字节为一组是半字对齐
-
short与int、int与char、double与任何数据类型均是字对齐
-
单独只有short与char是半字对齐
-
定义结构体变量时,要注意书写的变量顺序,避免大量内存浪费,影响效率