赞
踩
具有相同或不同类型元素的集合叫做结构体。定义一个结构体,本质是在制作一个类型:
// 声明一个学生信息结构体
struct Student
{
char name[20];
int age;
};
int main()
{
// 定义出两个学生变量
struct Student s1 = { "张三", 18};
struct Student s2 = { "李四", 20};
return 0;
}
在C中,结构体内只能存放各种类型的变量,不能存函数:
像上面这样就是声明了一个结构体struct Student
,此时的 struct Student
相当于一个类型名。
然后我们可以用这个自己声明的结构体类型去定义变量:
补充:C 和 C++ 中定义结构体变量的区别
也许初期看不习惯容易困惑,其实这就相当于两步合并一步:先定义结构体 struct Student,再定义变量 s1 和 s2:
使用方式:声明结构体的时候缺失结构体名,同时定义出一个或n个结构体变量:
这种形式只能使用在声明结构体的同时也定义出结构体变量,由于没有结构体名,因此后续不可以再定义新的结构体变量。
前面说过,使用结构体去定义结构体变量时,C 需要加 struct,C++ 不需要。那么使用结构体的别名去定义变量呢?
答:使用结构体别名去定义结构体变量时,C 和 C++ 都不需要加 struct,加了反而都会报错,因为取别名时把struct连同结构体名称一起包含进去了。
这种形式声明了一个缺失结构体名的结构体,但同时使用 typedef 为结构体设置了别名,所以之后我们可以使用这个别名,去定义结构体变量。
先弄清楚变量初始化和赋值的区别:
struct Student { char name[20]; int age; }; int main() { // 变量刚开始创建时给值,这个叫初始化 struct Student s1 = {"nick", 18}; // 变量创建后,再对它的值进行操作这个叫赋值 strcpy(s1.name, "tony"); s1.age = 24; return 0; }
结构体只能被整体初始化,不能被整体赋值,想要赋值的话只能把成员逐个地取出来再赋值。
补充:数组也是一样的道理:只能整体初始化,不能整体赋值。如果是字符数组想要整体赋值的话,可以使用 strcpy
函数:
本人推测结构体和数组不能被整体赋值的原因是:它们内部空间在逻辑上是独立一块块的,所以我们只能对这些独立的空间逐个赋值,而不能整体赋值。
我们可以通过变量或变量的地址去访问结构体的成员。
struct Student { char name[20]; int age; }; int main() { // 1、通过变量访问结构体成员 struct Student s; strcpy(s.name, "张三"); s.age = 18; // 2、通过指针访问结构体成员 struct Student* p = &s; printf("%s\n", p->name); printf("%d\n", p->age); return 0; } --------结果如下-------- 张三 18
为什么结构体会有两种访问方式?
在函数传参(传值、传址)时,会生成临时变量,如果要传的结构体变量太大的话,传值拷贝出来的临时对象也会很大,如果用传地址的方式来传结构体变量地址的话,可以很好的节省空间。
当然如果可以直接拿到结构体变量的话,使用变量来访问结构体成员会更直观点。
结构体的大小不是结构体元素单纯相加就行的,因为我们现在主流的计算机使用的都是 64 位字长的 CPU,对这类型的 CPU 取 8 个字节的数要比取一个字节要高效,也更方便。所以在结构体中每个成员的首地址都是8的整数倍的话,取数据元素时就会相对更高效,这就是内存对齐的由来。每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n)
来改变这一系数,其中的 n 就是你要指定的“对齐系数”。
PS:VS 中的默认对齐数为 8,不是所有编译器都有默认对齐数,当编译器没有默认对齐数的时候,成员变量的大小就是该成员的对齐数。
第一步:结构体中成员对齐
保证每个成员都放在一个对齐的地址上,对齐数 = min(该成员类型大小, 对其系数)
第二步:结构体整体对齐
保证结构体整体放在一个对齐的地址上,对齐数 = min(最大类型成员所长字节数,对其系数),看能否被第一步算出来的结构体的大小整除
第一步:找出每个成员变量的大小将其与编译器的默认对齐数相比较,取其较小值为该成员变量的对齐数
PS:这里使用的是VS编译器,故默认对齐数为8。
第二步:根据每个成员对应的对齐数画出它们在内存中的相对位置
第三步:通过最大对齐数决定最终该结构体的大小
通过图我们可以知道,绿色部分(double d成员占用)+红色部分(char c成员占用)+紫色部分(int i成员占用)+红色与紫色之间的白色部分(浪费掉了)总共占用了16个字节的内存空间。
我们需要将它们总共占用的内存空间(16)与结构体成员的最大对齐数(8)相比较,结构体的总大小为最大对齐数的整数倍,此时16正好是8的整数倍,所以该结构体在VS编译器下的大小就16个字节。即创建一个该类型的结构体变量,内存需为其开辟16个字节的内存空间。
PS:大多数情况下,成员变量已经占用的总字节个数并不一定正好为其成员变量中的最大对齐数的整数倍,这时我们需要将其扩大为最大对齐数的整数倍。
数组应拆开来看,不能看做一个整体
struct S
{
char a; //对齐数为1。占1个字节
char c[5]; //对齐数为1。可看成5个char占5个字节
int b; //对齐数为4。占4个字节,因为前面所有成员占6个字节,不是4
//个字节的整数倍,所以在第二个成员和第三个成员
//之间要补2个字节
} //所以该结构体的大小为1+5+2(补)+4=12个字节
1)如果结构体成员只是说明而没有定义变量,则这个结构体成员不占内存空间。
struct S
{
char a; //对齐数为1。占1个字节
struct s
{
int c;
char d;
}; //此处结构体只声明,没有定义结构体变量,所以该声明
//的结构体在地址空间中并不占位置
int f; //对齐数为4。占4个字节
double b; //对齐数为8,
}; //该结构体的大小为1+3(补)+4+8=16个字节
2)如果内部定义并申明了其他结构体变量,这时需要把这个结构体看成一个整体,大小要独立计算,至于对齐数取其内部最大成员的对齐数。
struct t
{
char a; //对齐数1
struct s //对齐数4
{
int c; //对齐数4
char d;//对齐数1
}g;//此处定义并申明了结构体变量,在这里需要把结构体
//看成一个整体,独立计算这个结构体的大小为8字节
//结构体整体的对齐数是内部最大成员的对齐数
//之后把这个结构体看出对齐数为4,大小为8的成员
char f; //对齐数1
int b; //对齐数4
}; //所以该结构体的大小为1+3(补)+8+1+3(补)+4=20个字节
联合体的大小等同于联合体里面最大成员的大小,所以可以把联合体等效成一个变量,这个变量就是联合体里面最大的那个成员。
和前文所说的结构体一样,如果只声明联合体,没定义联合体变量,则联合体就当成不存在。
struct t
{
char a;
union s
{
int c;
char d;
double h;
}g;
int f;
double b;
};//所以该结构体的大小为1+7(补)+8+4+4(补)+8=32个字节
1)在 VS2017 下测试
2)在 Centos7 下测试
在 c99 中有明确的规定允许结构体中最后一个数组大小是未知的。
struct T { int a; char b; int arr[];//或者int arr[0]; }; int main() { struct T t; // sizeof 求结构体大小时所求出的大小没有包括柔性数组的大小 printf("%lu\n", sizeof(struct T)); return 0; } --------结果如下-------- 8
包含柔性数组的结构体,可以把整个结构体看成是变长的。
#include<stdio.h> #include<stdlib.h> #include<stdlib.h> struct d { int nb; int nn; int arr[]; }; int main() { //分别给结构体中其他类型的成员和柔性数组申请空间 struct d *p=(struct d*)malloc(sizeof(struct d)+5*sizeof(int)); p->nb=100; p->nn=50; for(int i=0;i<5;i++) { p->arr[i]=i;//赋值 printf("%d ",p->arr[i]); } //重新调整所申请的空间,将柔性数组调整为40。 struct d *pp=(struct d*)realloc(p,48); if(pp!=NULL) { p=pp; for(int i=5;i<10;i++) { p->arr[i]=i;//赋值 printf("%d ",p->arr[i]); } free(p); p=NULL; } return 0; } --------结果如下-------- 0 1 2 3 4 5 6 7 8 9
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。