1、结构体声明
网站的建设创新互联公司专注网站定制,经验丰富,不做模板,主营网站定制开发.小程序定制开发,H5页面制作!给你焕然一新的设计体验!已为生料搅拌车等企业提供专业服务。2、结构体自引用
3、结构体变量的定义和初始化
4、结构体内存对齐
5、结构体传参
二、位段1、什么是位段
2、位段内存的分配问题
3、位段跨平台问题
一、结构体 在开发的过程中,我们难免要去描述一些复杂的对象,而想要描述这些对象,就不能使用之前学过的int,double
等这些类型。这时候就可以用到结构体了,结构体是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
1、结构体声明
下面是声明一个学生类型,例如:
struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
};
还有一种是结构体的特殊声明,就是可以不完全的声明。表示匿名结构体,即没有写结构体标签。就如下面这种形式:
//匿名结构体类型
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20],*p;
上述定义的两个结构体类型都是struct,没有对应的结构体标签,所以是匿名得结构体。 而匿名结构体因为没有结构体标签,每个struct 匿名结构体类型都是独立的,声明匿名结构体类型后,因为匿名结构体类型都是独立的。它只能在声明定义的时候在后面同时创建对应的结构体变量。
2、结构体自引用
结构体里面可以放相同或者不同类型的多个成员变量,那么结构体里面的成员变量可以是当前结构体类型创建的成员变量吗?答:是可以的。
首先先看一看上面的代码是否可以,答:这种写法是错误的,因为这个结构体类型里面的成员变量还没描述完就直接使用自身结构体类型创建个变量,这样是行不通的。
正确的结构体自引用方法是:
struct Node
{
int data;
struct Node* next;
};
这样写就能够明确结构体指针类型字节大小的,并且还能用这个结构体类型创建其他变量,例如创建一个s的变量,里面的结构体指针成员变量可以存放&s ,这个时候就可以存放这个结构体类型创建的另外的变量的地址,可以通过解引用访问到其他结构体变量。
3、结构体变量的定义和初始化
有了上面的结构体类型,那如何定义变量,其实很简单。
定义完变量就该初始化变量,如下:
//初始化:定义变量的同时赋初值
struct Book p3 = { x , y };
struct Book //类型声明
{
char BookName[20];//书名
char author[20];//作者
};
struct Book s = {"C语言程序设计","zhangsan" };//初始化
用struct Node类型创建了变量n1 和n2, 对n1和n2结构体变量嵌套进行初始化,对n1变量进行初始化时,当初始化到p这个struct Book变量时,因为其自身也是一个结构体变量,对结构体变量初始化里面成员可能有多个需要再嵌套一个{ },此时应该对该结构体变量里面的成员变量先进行初始化,最后在为最后一个结构体指针变量进行初始化为NULL,这就是结构体嵌套初始化。
4、结构体内存对齐
在前面我们已经掌握结构体的使用,但是我们还不清楚结构体大小的计算,想要计算结构体的大小,首先我们先要明白结构体内存的对齐,才能够准确的计算结构体的大小。
重点:计算结构体大小,首先我掌握结构体的对齐规则:
(1)第一个成员在与结构体变量偏移量为0的地址处。
(2)其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编辑器默认的一个对齐数与该成员大小的较小值。VS中默认对齐数为8。
(3)结构体总大小为大对齐数(每个成员变量都有一个对齐数)的整数倍。
(4)如果嵌套了结构体的情况,嵌套的结构体对齐到自己的大对齐数的整数倍处,结构体的整体大小就是所有大对齐数(含嵌套结构体的对齐数)的整数倍。
c1为结构体S1的第一个成员变量,所以对齐到偏移量0处,c1成员对齐数为1,并且占用1个字节,所以偏移量0开始的第一个内存单元就为c1的空间。
整形 i 为结构体的第二个成员变量,对齐到自身成员对齐数最小整数倍位置,偏移量为0处的内存单元已经存放了c1,所以需要往后到偏移量为4的位置,才是i成员对齐数4的倍数,即从偏移处4开始往后存放4个字节。
c2为结构体的第三个成员变量,对齐数为1个字节,当前可用的偏移量从8开始,而8是对齐数1的倍数,此时从偏移量为8的位置开始存放c2,c2占用一个字节,所以偏移8处就位c2存放的地址。此时,所有的成员变量都分配好空间,是从偏移量0到8,占用了9个字节,而结构体总大小为所有成员变量大对齐数4的倍数,9不是4的倍数,所以需要浪费3个字节,最终计算出的大小为12。
结构体S2的计算方法和S1相似,根据上图可以容易的计算出,在此处我就略写了。
为什么存在内存对齐? (有两个原因)
(1)平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特 定类型的数据,否则抛出硬件异常。
(2)性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
总体来说,结构体的内存对齐是拿空间来换取时间的做法。所以我们在设计结构体的时候,尽可能的让占用空间小的成员集中在一起。
5、结构体传参
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;
}
上述运用了两种传送形式,一种是直接传结构体,另一种是传地址。这两种方法我们选第二种传地址的方法,因为函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。 如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。
二、位段1、什么是位段
位段的声明和结构是类似的,有两个不同:
1.位段的成员必须是 int、unsigned int 或signed int 。
2.位段的成员名后边有一个冒号和一个数字。
上述A就是一个位段类型。此时计算结构体A的大小为8个字节。
因为一个int类型是4个字节 = 32个bit,所以我们先开辟一个int类型的4个字节32存放它们,a 占2个bit,b占5个bit,c占10个bit,此时,已经占用了17个bit,而d需要30个bit,这时放不下d,所以还需要再开辟一个int类型的4个字节存放d,所以结构体A的大小为8个字节。
2、位段内存的分配问题
1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
接下来我们通过例子能够更好地讲解位段内存的分配,并且还能清晰看出数据是怎么存放的。
下面我通过简单的画图可以帮助大家更好地理解。
3、位段跨平台问题
1. int 位段被当成有符号数还是无符号数是不确定的。
2. 位段中大位的数目不能确定。(16位机器大16,32位机器大32,写成27,在16位机 器会出问题。
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是 舍弃剩余的位还是利用,这是不确定的。
总结:跟结构体相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。
本文要是有不足的地方,欢迎大家在下面评论,我会在第一时间更正。
老铁们,记着点赞加关注哦!!!你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧