C语言结构体详解

1.结构体类型的声明

1.1什么是结构体呢?

结构体是⼀些值的 集合 ,这些值称为成员变量。结构体的 每个成员可以是不同类型的变量
值得注意的是 结构体类型 是一种变量 类型 !类比int、char等都是变量类型的一种。

1.2结构体的声明 

struct tag//结构体名
{
 member-list;
 //结构体成员
}variable-list;//结构体变量名

例如:

struct member
{
	char name[100];//名字
	int age;//年龄
	char gender[10];//性别
};

1.3结构体变量的创建和初始化

#include

struct member
{
	char name[100];//名字
	int age;//年龄
	char gender[10];//性别
};

int main()
{
	//按结构体成员顺序初始化
	struct member m1 = { "张三",23,"male" };
	printf("%s\n", m1.name);
	printf("%d\n", m1.age);
	printf("%s\n", m1.gender);

	//按指定顺序初始化
	struct member m2 = { .age = 21,.gender = "female",.name = "李四" };
	printf("%s\n", m2.name);
	printf("%d\n", m2.age);
	printf("%s\n", m2.gender);
	return 0;
}

1.4结构体自引用

结构体自引用的含义是结构体成员可以是结构体类型指针。如链表:

struct Node
{
	int data;
	struct Node* next;//结构体类型指针;
};

注意:不可是以下情况

struct Node
{
	int data;
	struct Node next;
};
因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的⼤⼩就会⽆穷的⼤,是不合理的。

2.结构体内存对齐

结构体如何使用我们已经了解了,那么计算结构体大小,就需要熟悉结 构体内存对齐

2.1对齐规则

⾸先得掌握结构体的对⻬规则:
  1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
  2. 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
  3. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍
  4. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的 整数倍
对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的 较⼩值
  • VS 中默认的值为 8
  • Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

2.2练习:

#include 

struct S1
{
	int a;
	char b;
	short c;
};

int main()
{
	printf("%zd\n", sizeof(struct S1));
	return 0;
}

C语言结构体详解_第1张图片

 最大对齐数是4,所以结构体S1的大小是4的倍数刚好为8。

struct S2
{
	char a;//1
	int b;//8
	short c;//2
};

int main()
{
	printf("%zd\n", sizeof(struct S2));
	return 0;
}

C语言结构体详解_第2张图片 

 结构体S2和S1仅是成员顺序的不同,但大小却不相同。S2最大对齐数是4,所以S2大小是4的倍数为12。

大家可以用这种画图的方式来理解结构体对齐规则。 

3.结构体传参

#include

struct S
{
	int data;
	int num;
};

void Print(struct S* s)
{
	printf("%d\n", s->data);
	printf("%d\n", s->num);
}

int main()
{
	struct S s1 = { 10,20 };
	Print(&s1);//指针传参
	return 0;
}
函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
如果传递⼀个结构体对象的时候,结构体过⼤,参数压栈的的系统开销⽐较⼤,所以会导致性能的下降。
结论: 结构体传参的时候,要传结构体的地址。

4.结构体实现位段

4.1什么是位段

位段的声明和结构是类似的,有两个不同:
  1. 位段的成员必须是 intunsigned int signed int ,在C99中位段成员的类型也可以选择其他类型。
  2. 位段的成员名后边有⼀个冒号和⼀个数字。

如:

struct A
{
	int _a : 2;
	int _b : 3;
	int _c : 10;
};

4.2位段内存分配

  1. 位段的成员可以是 int unsigned int signed int 或者是 char 等类型
  2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的⽅式来开辟的。
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。
struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};

int main()
{
	struct S s = { 0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
	return 0;
}

C语言结构体详解_第3张图片

4.3位段跨平台问题

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

总结:

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

 4.4位段使用注意事项

位段的⼏个成员共有同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位置处是没有地址的。内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。所以不能对位段的成员使⽤&操作符,这样就不能使⽤scanf直接给位段的成员输⼊值,只能是先输⼊放在⼀个变量中,然后赋值给位段的成员。
struct A
{
	int _a : 2;
	int _b : 3;
	int _c : 10;
};

int main()
{
	struct A sa = { 0 };
	scanf("%d", &sa._a);//错误示范

	//正确示范
	int a = 0;
	scanf("%d", &a);
	sa._a = a;
	return 0;
}

你可能感兴趣的:(c语言,开发语言)