网站开发简历模板,wordpress 内部链接,科技布沙发优缺点,上海设计网站建设自定义类型#xff1a;结构体进阶学习分享 前言1 结构体的基础知识2 结构的声明3 特殊声明4 结构的自引用5 结构体变量的定义和初始化6 结构体内存对齐6.1 计算结构体大小相关笔试题#xff08;基于VS#xff09;笔试题一#xff1a;笔试题二#xff1a; 6.2 为什么存在内… 自定义类型结构体进阶学习分享 前言1 结构体的基础知识2 结构的声明3 特殊声明4 结构的自引用5 结构体变量的定义和初始化6 结构体内存对齐6.1 计算结构体大小相关笔试题基于VS笔试题一笔试题二 6.2 为什么存在内存对齐6.3 如何设计结构体减少空间 1.7 修改默然对齐数8. 结构体传参9. 结尾 前言
结构体在C语言中具有重要的意义。它不仅可以封装和组织数据还可以提供抽象和封装的能力方便数据的传递和操作提高代码的可读性和可维护性是C语言中常用的数据类型之一。本篇博客将详细介绍相关知识。
1 结构体的基础知识
在结构体初阶我们以及详细介绍过了有关结构体的基础。 结构是一些值的集合这些值被称为成员变量。结构的每一个成员可以是不同类型的变量。 2 结构的声明
struct tag
{member-list;
}variabe-list;例如描述一个学生
struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
};3 特殊声明
在声明结构体的时候可以不完全声明。匿名结构体声明 比如
//匿名结构体
struct
{int a;char b;float c;
}x;
struct
{int a;char b;float c;
}*px;上面的两个结构在声明的时候省略掉了结构体标签tag。 那问题来了
//在上面代码的基础上下面的代码合法吗
px x;警告
编译器会将上面的两个声明当成完全不同的两个类型。所以是非法的。
4 结构的自引用
在结构中包含一个类型为该结构本身的成员是否可以呢
struct Node//err,比如计算其大小sizeof(struct Node)时大小是不确定的
{int date;struct Node next;
};//正确的自引用方法
struct Node
{int date;struct Node* next ;
};注意 上面结构体类型有时过于冗杂我们可以采用重命名。
//我们知道结构体可以匿名。那是否可以重命名呢
//答案是否定的。
//例如下面这段代码结构体中的Node* next变量还没有创建成功结构体不是完整的因此不可重命名
typedef struct//err
{int date;Node* next;
}Node;//正确使用
typedef struct Node
{int date;struct Node* next;
}Node;5 结构体变量的定义和初始化
有了结构体类型那如何定义和初始化呢其实很简单。
struct Stu //声明类型
{char name[15];//名字int age;//年龄
};
struct Stu s { zhangsan,20 };//初始化struct Point
{int x;int y;
};
struct Node
{int date;struct Point p;struct Node* next;
}n1 { 10, {4,5}, NULL };//结构体嵌套初始化6 结构体内存对齐
接下来我们将介绍一个热门考点结构体内存对齐 结构体内存对齐规则 1. 第一个成员在与结构体变量偏移量为0的地址处。 2. 其他成员变量要对齐到某个数字对齐数的整数倍的地址处。 对齐数 编译器默认的一个对齐数与该成员大小的 较小值 。 。vs默认的值为8 。Linux中没有默认对齐数对齐数就是成员自身的大小 3. 结构体的总大小为最大对齐数每个成员变量都有一个对齐数的整数倍。 4. 如果嵌套了结构体的情况嵌套的结构体对齐到自己的最大对齐数的整数倍处结构体的整体大小就是所有最大对齐数含嵌套结构体的对齐数的整数倍。 6.1 计算结构体大小相关笔试题基于VS
笔试题一
#include stdio.hstruct S1
{char c1;//对齐到偏移量为0处int i;//int占4个字节VS默认对齐数为8所以对齐数为最小值4字节//所以int对齐到偏移量为4后在向后移动4个字节即为。该位置即是int在内存中占据的空间char c2;//同理char的对齐数为1。在int后移动一个字节即可。
};
//上述结构体struct S1共占据9个字节而结构体的大小必须是最大对齐数4本题中对齐数分别为141的整数倍
//所以结构体还需向内存申请3个字节空间大小及总大小为12字节。struct S2
{char c1;//第一个变量成员对齐到偏移量为0处char c2; //该变量对齐数为1char为1byte, VS默认为9byte取最小值//即在变量c1后申请1字节即可int i;//该变量对齐数为4int为4byte, VS默认为9byte取最小值//所以变量i对齐到偏移量为4处在向后申请4个字节即可。
};//到此结构体总共向内存申请了8byte, 为最大对齐数4本题中三个变量对齐数分别为114的整数倍。//所以该结构体的最终大小为8int main()
{printf(%d\n, sizeof(struct S1));printf(%d\n, sizeof(struct S2));return 0;
}运行结果 —————————————————————————————————————
笔试题二
struct S3
{double d;//第一个成员变量对齐到偏移量为0处在向后申请8字节double大小为8char c;//该成员变量对齐数为1char为1byteVS默认对齐数为8取最小值//所以变量对齐到偏移量为8处向后申请1byteint i;//该成员变量对齐数为4int为4byteVS默认对齐数为8取最小值//所以该变量首先要对齐到偏移量为12的位置必须对齐到4的整数倍处在向后申请4个字节
};
//到此该结构体向内存共申请了16字节为最大对齐数8的整数倍
//所以该结构体的最终大小为16bytestruct S4
{char c1;//第一个成员变量对齐到偏移量为0处在向后申请1字节char大小为1struct S3 s3;//嵌套结构体结构体要对齐到自己最大对齐数8上面已经求得最大对齐数为8的整数倍处。即该成员变量对齐到偏移量为8出在向后申请16字节上面已经求出其大小16bytedouble d;//该成员变量对齐数为8double为8byteVS默认对齐数为8取最小值//上述两个变量总共为24个字节所以该成员变量从偏移量为24刚好为8得整数倍开始在向后申请8字节
};
//到此该结构体总共占据32个字节空间为最大对齐数8上述3个成员变量对齐数分别为188的整数倍。
//所以最终该结构体总大小为32byteint main()
{printf(%d\n, sizeof(struct S3));printf(%d\n, sizeof(struct S4));return 0;
}运行结果
6.2 为什么存在内存对齐
大部分参考资料是这么说的 1.平台原因移植原因) 不是所有的硬件平台都能访问任意地址上的任意数据某些硬件平台只能在某些地址处去某些特定类型的数据否则抛出硬件异常。 2. 性能原因 结构数据尤其是栈应尽可能地在自然边界上对齐。 原因在于为了访问未对其的内存处理器需要作两次内存访问而对齐的内存仅需要一次访问。 总的来说 结构体的内存对齐是拿空间来换时间的做法。 6.3 如何设计结构体减少空间
在设计结构体时我们既要满足对齐又要节省空间如何做到呢
我们前面已经见过这段代码
struct S1
{char c1;int i;char c2;
};
struct S2
{char c1;char c2; int i;
}
int main()
{printf(%d\n, sizeof(struct S3));//12printf(%d\n, sizeof(struct S4));//8return 0;
}S1和S2类型的成员一模一样但是S1和S2所占据的空间大小有一些区别。我们可以得到
在设计结构体时尽量让占用空间小的成员尽量集中在一起从而节省空间。
1.7 修改默然对齐数
#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));return 0;
}运行结果 总结
结构在对齐方式不合适说的时候我们可以自己更改默认对齐数。
8. 结构体传参
直接上代码
#include stdio.h
struct S
{int date[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* s)
{printf(%d\n, s-num);
}int main()
{print1(s);print2(s);return 0;
}上面print1和print2函数那个更好 答案是首选print2函数。 原因 函数传参的时候参数是需要压栈的会有时间和空间上的开销。 如果传递一个结构体对象的时候结构体过大参数压栈的系统开销比较大所以会导致性能的下降。 结论 结构体传参时要传结构体指针。 9. 结尾
本篇博客到此就结束了。传作不易如果对你有帮助记得三连哦感谢您的支持