结构体介绍(声明、成员的访问、自引用、初始化与定义、内存对齐、传参)

您所在的位置:网站首页 对结构体变量的成员可以像普通变量一样进行各种运算 结构体介绍(声明、成员的访问、自引用、初始化与定义、内存对齐、传参)

结构体介绍(声明、成员的访问、自引用、初始化与定义、内存对齐、传参)

2024-07-10 16:51| 来源: 网络整理| 查看: 265

目录

一、结构体的声明

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