网站建设合同约定三年后,徐州网站排名系统,如何建设网站教程,网页设计与网站开发教程大家好#xff0c;我们又见面了#xff0c;这一次我们来学习一些C语言有关于自定义类型的结构。
目录
1.结构体 2位段
1.结构体
前面我们已经学习了一些有关于结构体的知识#xff0c;现在我们进行深入的学习有关于它的知识。 结构是一些值的集合#xff0c;这些值称为…大家好我们又见面了这一次我们来学习一些C语言有关于自定义类型的结构。
目录
1.结构体 2位段
1.结构体
前面我们已经学习了一些有关于结构体的知识现在我们进行深入的学习有关于它的知识。 结构是一些值的集合这些值称为成员变量。结构的每个成员可以是不同类型的变量。 结构体的声明
struct tag
{
member-list;
}variable-list;我们要注意的是结构体的关键字是struct后面的就是我们自己定义的而括号里面的叫做成员变量它可以是任意类型的。例如我们自己创造一个学生结构体stu就是我们创建的结构体变量而名字年龄性别学号就是成员变量。 struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}; //分号不能丢匿名结构体
struct
{char neme[20];int age;char sex[5];//一个汉字2个字符float score;
}s1,s2;如上代码所示可以去掉结构体的名字 匿名结构体类型但只能用一次后面再想定义变量不可以(只可以使用s1和s2) 结构体的自引用 就是在结构中包含一个类型为该结构本身的成员 让我们看到下面这一段代码
struct Node
{
int data;
struct Node next;
};如果我们要算结构体的大小这段代码能够实现吗答案是否定的因为我们这个结构体中包含着下一个结构体我们要计算结构体大小的时候不仅是整型的大小还要加上下一个结构体的大小那么下一个结构体中又包含了一个结构体是无法计算的编译器会报错。那么正确的自引用是什么的样的呢 正确的自引用
struct Node
{
int data;
struct Node* next;
};这个代码可以运行用指针的话指针存放下一个节点的地址指针本身的大小也是固定的所以可以计算出来。 结构体变量的定义和初始化
结构体的定义
struct Point
{
int x;
int y;
}p1; //声明类型的同时定义变量p1struct Point p2; //定义结构体变量p2结构体的初始化
struct Point p3 {x, y};struct Stu //类型声明
{
char name[15];//名字
int age; //年龄
};
struct Stu s {zhangsan, 20};//初始化结构体内存对齐 第一个成员在与结构体变量偏移量为0的地址处。其他成员变量要对齐到某个数字对齐数的整数倍的地址处。 对齐数 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8结构体总大小为最大对齐数每个成员变量都有一个对齐数的整数倍。如果嵌套了结构体的情况嵌套的结构体对齐到自己的最大对齐数的整数倍处结构体的整 体大小就是所有最大对齐数含嵌套结构体的对齐数的整数倍。 这里我们定义两个结构体变量
struct S1
{char c1;int i;char c2;
};struct S2
{char c1;char c2;int i;
};
int main()
{/*printf(%d\n, offsetof(struct S2, c1));printf(%d\n, offsetof(struct S2, c2));printf(%d\n, offsetof(struct S2, i));*/printf(%d\n, sizeof(struct S1));printf(%d\n, sizeof(struct S2));return 0;
}我们想知道这两个结构体的大小那么一个整型是4个字节一个字符型是一个字节那么这两个结构体的大小是不是6个字节呢 很显然是不对的那为什么会造成这个结果呢那是因为其中成员对于起始位指定的偏移量造成的这里我们就要了解一个宏offsetof这个宏就是专门来计算偏移量的。
#includestdio.h
#include stddef.h
struct S1
{char c1;int i;char c2;
};struct S2
{char c1;char c2;int i;
};int main()
{printf(%d\n, offsetof(struct S1, c1));printf(%d\n, offsetof(struct S1, c2));printf(%d\n, offsetof(struct S1, i));printf(%d\n, offsetof(struct S2, c1));printf(%d\n, offsetof(struct S2, c2));printf(%d\n, offsetof(struct S2, i));/*printf(%d\n, sizeof(struct S1));printf(%d\n, sizeof(struct S2));*/return 0;
}在这里我们看到每个成员变量对于初始位置的偏移量都是不同的这就是因为内存对齐导致的。那么我们有什么办法来计算结构体的大小呢 这里以s2为例我们看到c1是一个字节和vs默认的八个对齐数所以c1的对齐数就是1第一个成员的偏移量为0所以c1就在下标为0的位置而i是四个字节和八个字节相比对齐数是4因为下标4才是4的倍数所以我们的i从下标为4的位置开始储存而c2的对齐数为1所以它的偏移量为i的倍数就直接放在i的后面因为结构体总大小为最大对齐数每个成员变量都有一个对齐数的整数倍最大是4所以是12. 那么我们为什么要存在内存对齐呢 平台原因(移植原因) 不是所有的硬件平台都能访问任意地址上的任意数据的某些硬件平台只能在某些地址处取某些特定类型的数据否则抛出硬件异常。性能原因 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于为了访问未对齐的内存处理器需要作两次内存访问而对齐的内存访问仅需要一次访问。 总体来说 结构体的内存对齐是拿空间来换取时间的做法。那在设计结构体的时候我们既要满足对齐又要节省空间如何做到让占用空间小的成员尽量集中在一起。 修改默认对齐数 #pragma 这个预处理指令这里我们再次使用可以改变我们的默认对齐数。 看到我们的代码
#include stdio.h
#pragma pack(8)//设置默认对齐数为8
struct S1
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的默认对齐数还原为默认#pragma pack(1)//设置默认对齐数为1
struct S2
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的默认对齐数还原为默认
int main()
{
//输出的结果是什么
printf(%d\n, sizeof(struct S1));
printf(%d\n, sizeof(struct S2));这里我们将默认对齐数改了之后所得出来的结果就明显的不同了根据我们使用的方法计算结果就是12和6。
结构体传参
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;
};那么我们位段的大小是多少呢
struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};int main()
{printf(%d\n, sizeof(struct A));return 0;
}这里我们看到程序运行的结果是8而我们根据自己的计算得到结构体占了47个比特位而每个字节占了8个比特位我们换算出来就是将近6个字节那么为什么不是6个字节呢这就要了解它的内存分布了。
位段的内存分配 位段的成员可以是 int unsigned int signed int 或者是 char 属于整形家族类型位段的空间上是按照需要以4个字节 int 或者1个字节 char 的方式来开辟的。位段涉及很多不确定因素位段是不跨平台的注重可移植的程序应该避免使用位段。 我们定义的位段它的类型的是char型而我们位段的内存分配是一个字节一个字节申请的我们先拿到一个字节的空间首先我们要给它三个比特位而且是从低地址到高地址分配的我们的a是等于10的它的二进制的前三位是010放到这个分配的字节中而后面也是一样b在这个字节中占了4个比特位然后这里只剩下一个比特位就放不下了就再申请一个字节的空间这个里面就拿来存放c的二进制而我们看到这个字节的空间就剩下了三个比特位了而d还要四个比特位的空间所以我们得再申请一个字节的空间。所以这个位段的大小就是三个字节我们在给二进制换算的十进制所以这三个字节的内存就是62 03 04。 位段的跨平台问题 int 位段被当成有符号数还是无符号数是不确定的。位段中最大位的数目不能确定。16位机器最大1632位机器最大32写成27在16位机器会出问题。位段中的成员在内存中从左向右分配还是从右向左分配标准尚未定义。当一个结构包含两个位段第二个位段成员比较大无法容纳于第一个位段剩余的位时是舍弃剩余的位还是利用这是不确定的。 总的来说跟结构相比位段可以达到同样的效果但是可以很好的节省空间但是有跨平台的问题存在。
好了今天的学习就到这里我们下次再见了。