C语言结构体解析

结构体声明

1.结构的基础知识

   结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

struct tag //结构体标签{ 
    member-list
    member-list //变量定义
    member-list  
    ...
} variable-list ;//结构变量

2.结构成员的类型

   结构的成员可以是标量、数组、指针、甚至是其他结构体。

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

struct Point{
	int x;
	int y;
}p1; //声明类型的同时定义变量p1

//初始化:定义变量的同时赋初值。
struct Point p3 = {x, y};

结构体内存对齐

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

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

结构体内存对齐练习:

struct S1
{
	char c1; //一个字节 4字节还有三个 sum = 1,rem = 3;
	int i;  //四个字节,还有三个不够,sum = 4 + 4,rem = 0;
	char c2; //一个字节,sum = 9,rem = 0;其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处3*4 = 12,sum = 12;
};
printf("%d\n", sizeof(struct S1)); // 12

struct S2
{
	char c1; //一个字节 4字节还有三个 sum = 1,rem = 3;
	char c2; //一个字节 3字节还有二个 sum = 1 + 1,rem = 2;
	int i; //四个字节 还有二个不够, sum = 4 + 4,rem = 0;其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处, 2 * 4 = 8,sum = 8;
};
printf("%d\n", sizeof(struct S2)); // 8

struct S3
{
	double d; //八个字节 sum = 8,rem = 0;
	char c;  //一个字节 sum = 8 + 1,rem = 3;
	int i;  //四个字节 3字节不够,sum = 8 + 4 + 4,rem = 0;
};
printf("%d\n", sizeof(struct S3)); //结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。2 * 8,16

struct S4
{
	char l; // 一个字节  sum = 1,rem = 3;
	struct S3 S3; //十六个字节  sum = 4 + 16,rem = 0;
	double d; // 八个字节  sum = 20 + 8 ,rem = 0;
};
printf("%d\n", sizeof(struct S4)); //结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。 16 * 2 ,32

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

结构体传参

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;
}

结论:函数传参的时候,参数是需要压栈的。如果传递一个结构体对象的时候,结构体过大。参数压栈得系统开销比较大,所以会导致性能下降。结构体传参的时候,要传结构体的地址。

位段

位段的声明和结构是类似的,有俩个不同点:
1.位段的成员必须是int 、unsigned int、signed int。
2.段位的成员名后边有一个冒号和一个数字。
比如

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

A就是一个位段,A的大小是多少?8

printf("%d\n",sizeof(struct A));//int 型具有四个字节,共32个比特位,故存储时将前三个放入第一块空间中,由于第四个是30个比特位存储不下,因此开辟下一快空间存储,即开辟了两快空间,就是好8个字节。其他类型同理可得。

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

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

发布了6 篇原创文章 · 获赞 0 · 访问量 156

猜你喜欢

转载自blog.csdn.net/famur/article/details/104434865