结构体介绍(声明、成员的访问、自引用、初始化与定义、内存对齐、传参) |
您所在的位置:网站首页 › 对结构体变量的成员可以像普通变量一样进行各种运算 › 结构体介绍(声明、成员的访问、自引用、初始化与定义、内存对齐、传参) |
目录 一、结构体的声明 1.1结构体的定义 1.2结构体的声明 1.3特殊的声明 二、成员的访问 2.1直接访问 2.2通过指针进行访问 三、自引用 三、初始化与定义 3.1创建结构体变量的两种方法 3.2结构体的初始化和嵌套初始化 四、结构体内存对齐 4.1内存对齐规则 4.2为什么要内存对齐 4.3成员顺序最优解 4.4修改默认对齐数 五、结构体传参 一、结构体的声明 1.1结构体的定义结构(体)是一些值的集合,这些值被称为成员变量。结构的每个成员变量可以具有不同类型。 结构的成员可以是标量、数组、指针,甚至是其他结构体。 1.2结构体的声明 struct tag { a; b; }c;以上即为结构体的声明格式 struct声明此处声明结构体 tag为结构体标签(即我们创建的结构体类型,类似于int x;里的int) a、b为结构体成员,可以为任何类型,此处成员数量可以为任意数。 c为我们在声明结构体时,一同创建的结构体变量,c为结构体变量的名称(根据创建位置分为全局变量和局部变量) 注:创建结构体时,最后的分号不要丢。 1.3特殊的声明结构体在声明时可以省略结构体标签(即上文提到的tag) struct { a; b; }c; struct tag { a; b; }d;此时称该声明为不完全声明,以后不可以再根据该次声明创建变量,只能在声明后创建一次变量(c和d) 注:虽然变量c和d的结构体内容完全相同,但是由于是不完全声明,系统会认为c和d为不同类型的变量。 二、成员的访问 2.1直接访问结构变量的成员是通过点操作符(.)访问的。点操作符接受两个操作数。 格式为 结构体变量名.结构体成员名 例如: struct tag { int a; char arr1[20]; }c; strcpy(c.arr1, "abcd"); c.a=20; printf("%d",c.a); 2.2通过指针进行访问有时候我们得到的不是一个结构体变量,而是指向一个结构体的指针。那该如何访问结构体成员呢? 当然我们可以先对指针进行解引用,再用(.)进行访问 struct tag { int a; char arr1[20]; }c; struct tag* p=&c; (*p).a=20;但是这样略显繁琐,有没有更简单的方式呢? 我们也可以通过->直接用指针访问成员 格式为 结构体指针->结构体成员名 例如 struct tag { int a; char arr1[20]; }c; struct tag* p=&c; p->a=20; 三、自引用在结构中包含一个类型为该结构本身的成员是否可以呢?(类似于函数的递归) struct tag { int a; struct tag b; };但是,我们会发现一个问题,这里的结构体是无线包含的,那么sizeof(struct tag)将无法计算,在运行代码时也无法为其分配内存,所以该方法显然是不行的。 那该怎样正确的自引用呢? struct tag { int a; struct tag* b; };将结构体内部的结构体改为结构体指针就可以很好的避免上述问题,完成自引用。 三、初始化与定义声明了结构体后,对变量的定义就很简单了。 3.1创建结构体变量的两种方法 struct tag { int a; char arr1[20]; }c;//方法一:直接跟在定义后面 //缺点:变量创建位置受限,无法自由创建全局变量/局部变量 //优点:省字数,可以快捷创建变量 struct tag d;//方法二 不受声明位置的限制,比较灵活 3.2结构体的初始化和嵌套初始化 struct tag1 //类型声明 { char arr1[15]; int a; }; struct Stu s = {"abedef", 20};//初始化 struct tag2 { int b; struct tag1 p; struct tag2* next; }n1 = {10, {"abedef",5}, NULL}; //结构体嵌套初始化 struct Node n2 = {20, {"abedef", 6}, NULL};//结构体嵌套初始化 四、结构体内存对齐声明了一个结构体之后,我们该如何定义结构体大小呢? 显然,直接将内部成员的所占的内存大小相加的计算方法是错误的 4.1内存对齐规则1. 第一个成员在与结构体变量偏移量为0的地址处。 2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 (根据不同编译器决定)与 该成员大小的较小值。 3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。 4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。 注:对于数组而言,它可以理解为一堆数组元素,所以它的对齐数为其中元素的大小与默认对齐数的较小值 4.2为什么要内存对齐(1)平台原因: 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特 定类型的数据,否则抛出硬件异常 (2)性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。 总的来说,结构体的内存对齐是拿空间来换取时间的做法。 4.3成员顺序最优解在设计结构体的时候,我们既要满足对齐,又要节省空间,就要让占用内存小的成员在一起,(尽量让占用内存大小相同的成员在一起) 例如 struct S1 { char c1; int i; char c2; }; struct S2 { char c1; char c2; int i; };s1占用12个字节,而s2占用8个字节,显然s2相较于s1更好。 4.4修改默认对齐数#pragma 这个预处理指令可以改变我们的默认对齐数 #pragma pack(8)//设置默认对齐数为8 #pragma pack()//取消设置的默认对齐数,还原为默认 五、结构体传参有两种传参方法:一种是传实参,另一种是传结构体变量的地址 请看代码 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; }如果要在二者中选一个的话,显然是print2更好的 因为如果直接传结构体的话,在函数内需要再次创建一个形参,而形参也会占用内存,如此就会对系统压栈造成压力,使系统性能下降。 所以,结构体传参要穿结构体的地址 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |