自定义结构体类型:struct使用详解

自定义结构体类型:struct使用详解


文章目录

  • 自定义结构体类型:struct使用详解
  • 一、结构体(struct)
    • 1、结构体的声明方法
    • 2、结构体的赋值
    • 结论:
    • 3、结构体的指针
    • 4、结构体的访问
    • 5、结构体内存对齐
    • 6、结构体位段
    • 结论:


一、结构体(struct)

在程序设计时,最重要的步骤之一就是选择表示数据的方法。在许多的情况下,简单变量甚至是数组还不足以表示一个事物的属性。为此,C语言提供了结构体变量(structure variable)提高表示数据的能力。
结构体是一些值的集合,这些值称为成员变量,把一些基本类型的数据组合在一起,形成一个新的复杂数据类型。结构体中的每个成员可以是不同类型的变量。
掌握:
1、为结构体建立一个格式或样式
2、声明一个合适的结构体变量
3、访问结构变量的各个部分

1、结构体的声明方法

结构体声明常用形式:

struct  tag
{
	member-list;	//成员变量,
}variable-list;		//;不可省略

定义结构体的三种形式 (推荐使用第一种)

例如:描述一本书(包括:书名、作者、价格等)
第一种:只定义一个新的数据类型,没有定义变量

struct book		//struct book 所起的作用相当于一般声明中的 int 或 float 
{
	char title[30];
	char author[30];
	float price;	
};	//分号不能省略,没有定义变量

struct book Jane_Eyre;	//在使用时再定义结构体变量 Jane_Eyre(英国文学名著《简·爱》作家夏洛蒂·勃朗特)

第二种:定义新的数据类型同时定义变量

struct book
{
	char title[30];
	char author[30];
	float price;	
}Jane_Eyre;		//分号不能省略,同时定义变量
						//声明的右花括号跟变量名

第三种:不完全声明的结构体

struct 			//匿名结构体类型,省略了结构体标签book
{
	char title[30];
	char author[30];
	float price;	
}Jane_Eyre;		//分号不能省略,同时定义变量

但是,如果打算多次使用结构模板,就要使用带标记的形式;或者,使用typedef函数。

2、结构体的赋值

我们知道初始化变量和数组的方法:

int sum =0;
int fibo[ 5 ] = { 1,1,2,3,5};

结构变量能不能这样初始化呢?答案是 :yes
初始化结构变量与初始化数组的语法语法类似:

方法一:定义变量的同时赋初始值

struct book
{
	char title[30];
	char author[30];
	float price;	
};

struct book Jane_Eyre = {"Jane_Eyre","Charlotte Bronte",35.5};

***************************************************************
struct book
{
	char title[30];
	char author[30];
	float price;	
} Jane_Eyre = {"Jane_Eyre","Charlotte Bronte",35.5};
***********************************************************************

使用一对{ }来对结构体进行初始化,各个初始化项用逗号隔开。
为了让初始化项与结构体体中的各个成员关联更加明显,我们可以让每个成员单独占一行。这样做的目的只是为了提高可读性,对于编译器本身而言并没有区别;

struct book masterwork ={
						"the Old Man and the Sea",
						"Hemingway",
						32.4
						};

方法二:逐个成员赋值

struct book Jane_Eyre = {
						.title = "Jane_Eyre",
						.price = 35.5;
						.author="Charlotte Bronte"
						};
*********************************************************
struct book Jane_Eyre = {
						.price = 35.5;
						};

1. 逐个成员赋值时,可以只初始化其中的某一个成员;
2. 逐个成员赋值时,赋值的顺序可以是任意的;
3. 如果有多次赋值,最后一次赋值才是该成员变量真实的值;

方法三:整体赋值

struct book Jane_Eyre = {
     
						.title = "Jane_Eyre",
						.price = 35.5;
						.author="Charlotte Bronte"
						}//定义了一本书:乌托邦
struct book Utopia  =  Jane_Eyre;

1)相同数据类型结构体变量可以直接赋值

结论:

1)凡是基本数据类型,既可以定义时初始化,也可以先定义,再赋值
2)凡是构造类型,要么在定义时初始化(定义的时候可以整体赋值);
不可以,先定义再以初始化的方式初始化;(如果定义完之后,只能单个的赋值)

3、结构体的指针

在讲述结构体成员的访问之前,我们先来讲述一下结构体的指针,因为在结构体成员访问时也可以通过结构体指针来访问;
喜欢用指针的人一般都乐于使用结构体指针,因为:

1. 就像指向数组的指针比数组本身更容易操控;
2. 传递给函数时,传递结构体指针效率更高;
3. 在表示数据的结构体中可能包含指向其他结构的指针

//可以声明的同时定义
struct book	
{
     
	char title[30];
	char author[30];
	float price;	
} * book1,book2;
//也可以先声明,后定义
struct book * book3 = & book2

和数组不同,结构名并不是结构体的地址,因此要在结构名前加上 & 运算符

4、结构体的访问

结构体的访问有两种形式,一种是:结构体变量名 . 成员名 ;二种是:指针变量名 ->成员名

方法一:结构体变量名 . 成员名

struct book	
{
     
	char title[30];
	char author[30];
	float price;	
};

int main()
{
     
	struct book	book2 =  {
     "Jane_Eyre","Charlotte Bronte",35.5}printf("%s\n",book2.title);
	printf("%s\n",book2.author);
	printf("%f\n",book2.price);
	return 0;
}

方法二:指针变量名 ->成员名

int main()
{
     
	struct book	* book1 =  &book2;
	printf("%s\n",book1->title);
	printf("%s\n",book1->author);
	printf("%f\n",book1->price);
	return 0;
}

//  book1->title  == (*book1) . title == book2.title

第二种方法是最为常用的方法:使用 针变量名 ->成员名 ,在计算机内部会被转化成 (*针变量名).成员名 的方式来执行,所以 book1->title 等价于 (*book1) . title 等价于 book2.title;
所以说这两种方式是等价的

5、结构体内存对齐

首先掌握结构体内存的对齐规则:

  1. 第一个成员在与结构体变量偏移为0的地址处;
  2. 其他成员变量要对齐到 对齐数的整数倍;(对齐数:编译器默认的对齐数与该成员大小的较少值)
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数的整数倍(包括嵌套结构体的对齐数);

知道了理论,下面开始举例
以下面为例:

struct S1
{
     
	char c;
	int i;
	double d;
};

第一步:第一个成员在与结构体变量偏移为0的地址处;![在这里插入图片描述](https://i

自定义结构体类型:struct使用详解_第1张图片
第二步:其他成员变量要对齐到 对齐数的整数倍;(对齐数:编译器默认的对齐数与该成员大小的较少值)
自定义结构体类型:struct使用详解_第2张图片
第三步: 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
自定义结构体类型:struct使用详解_第3张图片
如果嵌套了结构体的情况,嵌套的结构体对齐到自己最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数的整数倍(包括嵌套结构体的对齐数)。

内存对齐的意义:

1、平台原因(移植原因):各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐

2、性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那 么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。显然在读取效率上下降很多。

设计结构体变量时:将占用空间小的成员集中在一起,在一定程度可以避免内存的浪费。

自定义结构体类型:struct使用详解_第4张图片
可见,相同的成员变量,不同的顺序,也会影响结构体的大小;

6、结构体位段

位段的声明和结构体的声明基本相同,但有两个不同;
1、位段的成员必须是整形家族的(int 、unsigned int、singned int、char)
2、位段的成员后有一个冒号和一个数字;

struct s1
{
     
	int _a:2;
	int _b:5;
	int _c:10;
	int _d:30;
}

一个int 类型占用4个字节(共32个比特位),int _a:2 申请了一个int型的变量,:6 表示存储这个数据的空间只占用了6个比特位。所以位段的使用可以使得结构体的空间变小,但伴随而来的就是每个成员变量存储空间的改变,和平台可移植性的问题。

正常情况下每个int型变量可以存储的数据是2^32个,但int _a:2实际上只使用了两个比特位,实际可以表示的数据为2^2个;
在这里插入图片描述
位段的跨平台问题:
1、int位段被当成有符号数还是无符号数是不确定的;
2、位段中最大位的数目不能确定(16位机器最大16,32位机器最大32,如果在32位机器上上使用一个超过16位的数据,移植到16位机器中时,就有可能会出现问题)
3、位段中的成员在内存中是从左往右分配,还是从右往左分配没有一个标准的定义(因此只画出内存使用比特位数的示意图,具体使用情况视平台而定)
4、当一个结构博包含两个位段,第二个位段成员比较大时,无法容纳于第一个位段剩余位时,剩余的位是舍弃还是利用,是不确定的。

结论:

跟结构体相比,位段可以达到相同的效果。可以在一定程度上节省空间,但是会有跨平台的问题出现。

你可能感兴趣的:(编程语言)