结构体

1.什么是结构体?

结构体就是类似于名片(一个公司为员工做的统一样式的模板的名片,里面包含名字,职务,电话等,但内容是空的,等员工调用时才输入内容,分配空间)形式的数据结合体,可以把它理解为一种由用户自定义的特殊的复合型的“数据类型”,在这个复合型的数据类型中可以包含多个基本类型,我们可以把它当成一个整体来操作。

总结:结构体是多种类型的数据的集合;

           结构体是一种由用户自定义的特殊的复合型的“数据类型”;

           结构体类似于用于制作统一样式名片的空白模板,或图纸。

2.结构体的声明:

结构体的声明是一种“数据类型”的声明,并不是定义对象(变量)实体。

struct Student 是结构体Student这个复合型“数据类型”的类型名,其使用方式如同表示 int 。

3.结构体对象的定义及初始化

结构体的声明只是定义了一种数据类型,并没有定义实体对象,并没有为其分配空间;

结构体变量的定义和基本类型变量的定义是一样的 : 类型名+变量名;

结构体变量可以在定义的同时对成员进行初始化赋值,把各个结构体成员的初始值依次排列在花括号中,并用逗号隔开。例如: struct Student {            //声明结构体 Student

                                                  char name[12];

                                                  char sex[3];

                                             }tony={"Tony","男"};       //定义结构体对象tony并初始化

4.结构体对象成员的访问

结构体变量是由结构体成员变量组成的变量集合体,并且像数组一样存储在连续的内存空间中,因而可以单独访问其中的成员变量;访问结构体成员变量时用“.”,称为句点运算符,具体形式为“对象名.成员名”

在设计一个结构体时,不能给结构体中的成员变量赋值,不会给结构体分配空间,相当于它只是个图纸。举个例子:

struct student                       //student为结构体名,struct student 是结构体类型名;

{                                           //属性//数据成员描述;

      char s_id[10];

      char s_name[10];

      char s_sex[8];            

      int s_age;                        //不能给age,id,等赋值;

}                                        //     在这里设计的结构体不会开辟空间,

int main()

{

struct student s1;              //在这里才给s1开辟空间;

}

比较数组和结构体:  

相同:1.都是用花括号初始化;例如:struct student s1={ "007","longgege","man","20"}; a[10]={1,2};

           2.初始化时 未初始化的值数组与结构体都一律赋0值;

不同:1.数组名代表的是数组的首地址,而结构体名代表他本身;

           2.结构体变量可以相互初始化,例如:struct student s2=s1;(前提条件是两个结构体变量类型相同),但是数组不能,因为地址不能相互赋值,一个地址不能被改变。

结构体的存储

对于存储变量地址对齐的问题,计算结构体大小的3条规则:

1.结构体变量的首地址必须是结构体变量中“最大基本类型成员所占的字节数”的整数倍。

2.结构体变量中的每个成员对于结构体首地址的偏移量,都是该成员基本类型所占字节数的整数倍。

3.结构体变量的总大小必须是结构体变量中“最大基本类型所占字节数的”整数倍。

注:在计算结构体变量大小时,数组和嵌套结构体变量都拆成基本类型后再来计算。

例如:

它的大小我第一次计算错误的原因就是把嵌套的结构体当作是一个基本类型

即 :  10+8=18->24(嵌套结构体的整数倍)+12=36+8=44;

改正: 10+8=18->20(嵌套结构体中最大基本类型为int型)+12=32+8=40(该结构体中最大基本类型为double 型,40是它的整数倍,所以该结构体变量大小为40bit)。 

为什么要理解字节对齐问题:

(1)内存大小的基本单位是字节(byte),从理论上来讲可以从任意地址访问变量,但是实际上,cpu并非逐字节读写内存,而是以2,4,或8的倍数的字节块来读写内存,因此就会对基本类型数据的地址做出一些限制,即他的地址必须是2,4或8的倍数的字节。那么就要求各种类型和数据按照一定的规则在空间上排列,这就是对齐;

(2)有些平台每次都都是从偶地址开始,如果一个int型(假设为32为系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要读两个周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据,显然在读取效率上下降很多;

(3)由于不同平台对齐方式可能不同,如此一来,同样的结构体在不同平台的大小可能不同,在无意识的情况下互相发送的数据可能发生错乱;

解决办法:指定对齐值

预处理指令 #pragma pack(n) 可以改变默认对齐数。n的取值是,1,2,4,8,16。

指定对齐值后 偏移量和总大小都是根据对齐值来计算,例如:

#pragma pack(1)

struct node

{

    char a;

    int b;

    short c;

};

#pragma pack()   //#pragma pack(n) 需要以#pragma pack()作结束,表示该种对齐方式到此为止。

那么这个结构体大小为 1+4+2=7;

若在VS中默认的对齐值计算:1->4+4=8+2=10->12 所以不同的对齐值计算出的结构体大小是不同的。