【31】C语言 | 自定义类型 | 结构体
创始人
2024-05-21 03:31:18
0

目录

结构体

1【结构体类型的声明】

2【结构体类型的声明】

3【结构体变量的定义和初始化】

4【结构体内存对齐】

5【结构体传参】

6【结构体实现位段的能力 】


1、结构体

  • 生活中对象是复杂的,比如书、书名、作者、出版社、定价、书号等。怎么描述这一类对象呢?于是就出现了结构体。
  • 结构是一些值的集合,这些值称为成员变量,结构的每个成员可以是不同类型的变量。

1【结构体类型的声明】

struct tag
{member-list;
}variable-list;

 【举例理解一下:】

//相当于自己创建了一个类型Book
struct Book    //struct 是结构体的关键字,Book是结构体的标签名
{char name[20];   // {}里面放的是描述这个对象的成员变量列表int price;char id[12];
}b3,b4,b5;           // b3,b4,b5这里创建的实体是全局的,而b1,b2在{}里面所以是局部的
int main()
{struct Book b1;  //拿自己创建的类型Book创建了个b1,b1就是一个实体struct Book b2;return 0;
}

【不完全声明】

//匿名结构体类型,只能用一次,用完就不能用了
struct 
{char c;int i;char ch;
}s;

2【结构体类型的声明】

结构体类型的声明:就是结构体包含结构体

【举例理解一下】 

struct A
{int ret;char w;
};struct B
{char t;struct A aa; //结构体B包含了结构体Adouble f;
}
int main()
{return 0;
}

【结构体的自引用】

struct Node
{int data;struct Node next;
};
//这种是错误的引用
//结构体不能引用同一结构体的成员变量,而是引用同一结构体的指针struct Node
{int data;struct Node* next;
};
//这种引用同一结构体的指针是正确的

3【结构体变量的定义和初始化】

【举例理解一下】

struct A
{int x;char y;
}p1;                 // 声明类型的同时定义变量p1struct B
{double b;struct A s;
};
int main()
{struct A p2;     // 定义结构体变量p2struct A P3 = {0,'c'};//初始化:定义变量的同时赋初值struct B p4 = {3.14,{0,'y'}};//. 是针对结构体变量的//->是针对结构体指针的printf("%lf %d %c\n",p4.b,p4.s.x,p4.s.y);return 0;
}

4【结构体内存对齐】

先看下面代码,结构体的大小是多少

struct  S
{char c1;int i;char c2;
};int main()
{struct S s = {0};printf("%d\n",sizeof(s));return 0;
}
//输出为12

输出为:12,为什么是12呢,然后引出结构体内存对齐

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

下图:

  • 1 + 3(浪费) + 4 + 1 + 3(浪费)  = 12
  • 12刚好是最大对其数4的整数倍
  • 所以就是12

C1

i

C2

 为什么存在内存对齐?
大部分的参考资料都是如是说的:

  • 1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  • 2.性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问,而对齐的内存访问仅需要一次访问。

总体来说
结构体的内存对齐是拿空间来换取时间的做法

【指定对齐值】 

  • #pragma pack (n),C 编译器将按照 n 个字节对齐。
  • #pragma pack (),取消自定义字节对齐方式。 
#pragma pack(2)
struct  S
{char c1;int i;char c2;
};
#pragma pack()
int main()
{struct S s = {0};printf("%d\n",sizeof(s));return 0;
}//输出为8

【offsetof宏】

#include
struct  S
{char c1;int i;char c2;
};int main()
{printf("%d\n",offsetof(struct S,c1));printf("%d\n",offsetof(struct S,i));printf("%d\n",offsetof(struct S,c2));return 0;
}//输出为://0//4//8//请按任意键继续. .

5【结构体传参】

下面的print1和print2函数哪个好些?

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函数。原因:

  • 函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
  • 如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。

结论:结构体传参的时候,要传结构体的地址。

6【结构体实现位段的能力 】

位段的内存分配

  • 1.位段的成员可以是int unsigned int signed int 或者是char (属于整形家族)类型。
  • 2.位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
  • 3.位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

位段的跨平台问题

  • 1.int位段被当成有符号数还是无符号数是不确定的
  • 2.位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机
  • 器会出问题。
  • 3位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。4.当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。 

跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。

相关内容

热门资讯

中证A500ETF摩根(560... 8月22日,截止午间收盘,中证A500ETF摩根(560530)涨1.19%,报1.106元,成交额...
A500ETF易方达(1593... 8月22日,截止午间收盘,A500ETF易方达(159361)涨1.28%,报1.104元,成交额1...
何小鹏斥资约2.5亿港元增持小... 每经记者|孙磊    每经编辑|裴健如 8月21日晚间,小鹏汽车发布公告称,公司联...
中证500ETF基金(1593... 8月22日,截止午间收盘,中证500ETF基金(159337)涨0.94%,报1.509元,成交额2...
中证A500ETF华安(159... 8月22日,截止午间收盘,中证A500ETF华安(159359)涨1.15%,报1.139元,成交额...
科创AIETF(588790)... 8月22日,截止午间收盘,科创AIETF(588790)涨4.83%,报0.760元,成交额6.98...
创业板50ETF嘉实(1593... 8月22日,截止午间收盘,创业板50ETF嘉实(159373)涨2.61%,报1.296元,成交额1...
港股异动丨航空股大幅走低 中国... 港股航空股大幅下跌,其中,中国国航跌近7%表现最弱,中国东方航空跌近5%,中国南方航空跌超3%,美兰...
电网设备ETF(159326)... 8月22日,截止午间收盘,电网设备ETF(159326)跌0.25%,报1.198元,成交额409....
红利ETF国企(530880)... 8月22日,截止午间收盘,红利ETF国企(530880)跌0.67%,报1.034元,成交额29.0...