【C语言】结构体

1.结构体基本概念

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

2.声明

eg声明一个学生信息的结构体。

struct Stu
{
   char name[20];
   short int age;
   char sex[5];
}stu1,stu2;//stu1,stu2为变量列表,并且为全局变量,分好不能丢

int main()
{
   stu1.age=10;
   printf("%d\n",stu1.age);
   return 0;
}

为了使用方便,并且避免全局变量的使用,一般如下使用

typedef struct Stu
{
   char name[20];
   short int age;
   char sex[5];
}Stu;//分号不能丢

int main()
{
   Stu stu1;
   printf("%d\n",stu1.age);
   return 0;
}
声明结构体时并未占用内存空间,当使用Stu时才会占用内存空间。(即实例化时才会占用内存)。

3.匿名结构体(无类型名)

struct 
{
   char name[20];
   short int age;
   char sex[5];
}S;//分号不能丢
struct 
{
   char name[20];
   short int age;
   char sex[5];
}*pS;//分号不能丢

虽然成员内容相同,但是编译器会把上面的两个声明当做完全不同的两个类型,即代码pS=&S;不合法。

4.结构成员的访问

.的访问方式 (针对结构体变量)

struct Stu
{
   char name[20];
   short int age;
   char sex[5];
};//分号不能丢
int main()
{
  struct Stu s;
  strcpy(s.name."fangqi");
  s.age=29;
  strcpy(s.sex,"男");
 }

->的方式 (针对结构体指针,找它指向对象的成员)

struct Stu
{
   char name[20];
   short int age;
   char sex[5];
};//分号不能丢
int main()
{
  struct Stu s;
  struct Stu* ps=&s;
  strcpy(s.name."fangqi");
  s.age=29;
  strcpy(s.sex,"男");
  printf("%s\",ps->name);
  printf("%d\n",ps->age);
 }

5.结构体的自引用

struct Node
{
  int data;
  struct Node* n;
};

6.结构体的不完整声明

struct A;//先定义一个不完整声明,解决下面问题。
struct A
{
 int a;
 struct B* b;
};
struct B
{
 int b;
 struct A* a;
};

7.结构体的定义与初始化

struct S
{
 int a;
 double a;
 char a[10];
}s1;//创建类型时直接定义,s1为全局变量
//struct S s2;该s2也为全局变量
int main()
{
 struct S s2 = {20,3.14,"hehe"};//类型创建完成再定义,s2为局部变量,{}用来初始化
 return 0;
 }

8.结构体内存对齐

1)基本概念

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

//offsetof();可以测试偏移量 头文件#include<stddef.h>
//define offsetof(s,m) (size_t)&(((s*)0)->m)

struct s1
{
    int a;
	char c1;
	char c2;
};

int main()
{
	printf("%d ", offsetof(struct S, a));
	printf("%d ", offsetof(struct S, c1));
	printf("%d ", offsetof(struct S, c2));
	system("pause");
	return 0;
}

运行结果为:0 4 5

2)练习

struct s1
{
    int a;//4
	char c1;//1
	char c2;//1
};

int main()
{
    printf("%d",sizeof(struct s1);//输出为8
}

由内存对齐规则第一点得0~3为a的内存存储,由第二点c1对齐数为(8与1)的较小值为1,得4为c1的存储,c2同c1,由第三点得结构体大小必须为成员最大对齐数整数倍即为4的整数倍,得该结构体字节为8。
2.

struct S1
{
	double d;//0~7
    char c;//8
	int i;//12-15
};
struct S2
{
    char c1;//0
	struct S1 s1;//8~23
	double d;24~31
};
int main()
{
    printf("%d",sizeof(struct S2);//输出为32
}

由内存对齐规则第一点得0为c1的内存存储,由第四点得S1的最大对齐数为8则S1为823,由第二点得d为2431,所以S2字节为32。

3)为什么存在内存对齐?

1.移植原因(平台原因)
不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2.性能原因
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存访问仅需要一次访问。(牺牲空间换取时间)
所以设计结构体的时候尽量将空间小的成员集中在一起。

4)设置默认对齐数

#pragma pack(设置对齐数)
结构体
#pragma pack()

eg:

#pragma  pack(4)
struct S
{
	char c;
	double b;
	char f;
};
#pragma  pack()

int main()
{
	printf("%d ", sizeof(struct S));
	system("pause");
	return 0;
}

9.结构体传参

比较值传递与地址传递
GetTickCount();函数是#include<windows.h>计算时间

#include <stdio.h>
#include <stdlib.h>
#include<Windows.h>


struct S
{
	char name[20];
	int age;
};
void print(struct S s)
{
	printf("%s %d\n", s.name, s.age);
}
void print2(const  struct S* ps)
{
	printf("%s %d\n", ps->name, ps->age);
}

int main()
{
	struct S stu = { "zhang", 20 };
	int i = 0;
	int start1 = 0;
	int end1 = 0;
	int start2 = 0;
	int end2 = 0;
	start1 = GetTickCount();
	for (i = 0; i < 1000;i++)
	   print(stu);
	end1 = GetTickCount();
	start2 = GetTickCount();
	for (i = 0; i < 1000; i++)
	   print2(&stu);//效率高
	end2 = GetTickCount();
	printf("%d\n", end2 - start2);
	printf("%d\n", end1 - start1);	
	system("pause");
	return 0;
}

运行结果为:传值为438毫秒,传地址为203毫秒所以选择地址传参
这里写图片描述

10.位段

1)概念

1.位段的成员必须是 int 、unsigned int或者 signed int.
2.位段的成员名后面有一个冒号和一个数字

eg:

struct A
{
  int _a:2;//2就是位指的是2个比特位
  int _b:5;
  int _c:10;
  int _d:30;
};

A就是一个位段类型。
A的大小是8个字节Beacuse:为a创建4个字节32个比特位,使用2个还有30个,b使用5个,c使用10个,d不够再创建4个字节。c语言未规定是否使用上面剩余的还是浪费,(vs是浪费的)所以位段不跨平台。

2)位段的内存分配

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

3)内存对齐

1、同类型的、相邻的可连续在一个类型的存储空间中存放的位段成员作为一个该类型的成员变量来对待

2、不是同类型的、相邻的位段成员,分别当作一个单独得该类型的成员来对待,分配一个完整的类型空间,其长度为该类型的长度,其他成员的分配规则不变,仍然按照前述的对齐规则进行。

struct A  
{
    unsigned int a:1; //与后面类型不一致,分配类型空间 +4字节
    unsigned char b:1;//与前类型不一致,分配类型空间   +1字节
    unsigned int c:1; //与前类型不一致,分配类型空间,从自然边界算起。从第8字节起,+4
    unsigned int d:2; //与前类型一致,不超过类型位数
    unsigned int e:2; //与前类型一致,不超过类型位数
}S;

故S4大小为12字节。(vs)

4)位段的跨平台问题

1.int位段被当成有符号数还是无符号数是不确定的。
2.位段中最大位的数目不能确定。(16位机器最大16,32为机器最大32,写成27,在16位机器会出问题)
3.位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4.当一个结构包含两个位段,第二个位段成员比较大,无法容忍于第一个位段剩余的位时,时舍弃剩余的位还是利用,这是不确定的。
位段与结构体相比,可以达到相同的效果,但是可以节省空间,并有跨平台问题存在。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值