前言:
在初学者学习C语言,前期一般都会使用内置类型也就是(int char doublu),但是如果我们要表现一些差异化的物体,单单一个Int和char 已经无法表示。在这个时候就会用到我们的结构体了,也就是平时说的自定义类型。
你可以把结构体理解为内置类型和自定义类型的结合体,他是由各种类型组成的!
他是这些成员的集合,每个成员可以是不同的类型!
1.结构体的声明
typedef struct Stu
{
char name[20];//名字
int age;//年龄
char sex[8];//性别
}Stu;
这就是一个结构体的声明,我们要表示一个学生的时候,不能单单用一个int或者char 来表示,人是一个多样的物种,需要具体表示一个学生,我们需要用他的名字,年龄,性别等等来表示,所以我们就需要不同类型的集合表示。
定义:
struct Stu
{
char name[20];
int age;
char sex[6];
}s1;//定义的全局变量
struct Stu s2;//全局变量
int main()
{
struct Stu s3;//局部变量
return 0;
}
看上面代码,如果没有给结构体前重命名(typedef),那么结构体后面的s1就是一个全局的变量,s2.s3前面就是类名(struct Stu)。当然,还有一种是结构体重命名后的定义,看下方代码
typedef struct Stu
{
char name[20];
int age;
char sex[6];
}Stu;//结构体重命名为Stu
Stu s2;//全局变量
int main()
{
Stu s3;//局部变量
return 0;
我们看到上方代码与之前有一些不同,当结构体重命名后,结构体定义的时候,就直接用重命名后的类名去定义对象。
初始化:
struct Stu
{
char name[20];
int age;
char sex[6];
};
int main()
{
struct Stu s1 = { "zhangsan",18,"nan" };
struct Stu s2 = { {"zhangsan"}};
return 0;
}
s1用{ }大括号按照顺序全部初始化,s2只是初始化了一个,剩下的默认初始化为0;
2.结构体访问
我们要访问结构体对象中的成员时候,我们就需要用到两个操作符好 . 与 -> 那么他们有什么不同呢,看下面代码
struct Stu
{
char name[20];
int age;
char sex[6];
};
void Print(struct Stu* s2)
{
printf("%d", s2->age);
}
int main()
{
struct Stu s1 = { "zhangsan",18,"nan" };
printf("%d\n", s1.age);
Print(&s1);
return 0;
}
我们看到了两个操作符运用的不同场景,但是他们有什么区别呢?
操作符 . :如果我们直接用结构体对象(s1)去访问成员那么就用 . s1.成员名
操作符 -> : 如果我们用结构体指针(s2)去访问成员那么就用 -> s1->成员名
其实理解起来很简单,如果是指针就用-> 结构体对象就用 .
在这里我要说一个需要注意的点,如果结构体要作为参数传参,那么尽量传递他的地址,这样会减少开销,如果传结构体对象,那么接收的时候就要产生拷贝,如果结构体过大,就会产生大量的开销。
3.结构体的大小
结构体大小是一个老生常谈的问题了,他也是一个非常非常热门的考点!为啥一个计算大小能成为考点呢?
这就不得不说结构体的内存对齐机制了,内存对齐.有两个原因:1.平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特 定类型的数据,否则抛出硬件异常。
2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
总结一下:
内存对齐就是以空间换时间的做法,通过内存对齐提高效率
struct S1
{
char c1;
int i;
char c2;
};
struct S2
{
char c1;
char c2;
int i;
};
int main()
{
printf("%d\n", sizeof(struct S1));
printf("%d\n", sizeof(struct S2));
return 0;
}
我们可以看到上面的结构体成员是一摸一样的,要说不同,就只有顺序上的不同。那么大家觉得他们的大小是否一样呢?
执行后,我们发现两个结构体大小居然是不同的,那么为什么会出现这样的情况呢?
我们要看懂上面的计算过程,我们就要了解内存对齐的规则:
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
首先大小都是从0开始计算,下一个要对齐到对齐数整数倍的地方(对齐数 与 该成员大小的较小值)如果对齐不上中间就会补上空,计算完后结构体的大小后必须成员最大对齐数的整数倍,
不够也得补上空!!
修改默认对齐数:
#pragma pack(4)
struct S1
{
char c1;
int i;
char c2;
};
#pragma pack()
那么,在这里上面的#pragma pack(4)是什么意思呢,其实也很简单,就是把默认对齐数修改为4(vs默认的对齐数是8),那下面的#pragma pack()又是什么呢?他表示的就是还原默认对齐数(8)
上就是全部代码,如有错误和不足的地方。欢迎大家指正,下一节我还会细谈一下谈下结构体,请大家关注一下!!!