目录

结构体

结构体与数组

结构体成员类型

结构体的声明

结构体自引用

结构体的定义和初始化

结构体成员的访问

结构体内存对齐

结构体的对齐规则

为什么要内存对齐

计算成员变量相对于结构体类型的偏移量

结构体传参

位段

举例:

位段的内存分配

关于位段类型我的理解(VS编译器)

位段的跨平台问题

结构体

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

结构体与数组

  • 数组:一组相同类型元素的集合
  • 结构体:也是一些值得集合,但是值的类型可以不同

注意:结构体类型仅仅是一种数据类型,也可以存在结构体的数组

结构体成员类型

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

结构体的声明

//1
struct Stu
{   //结构的成员变量char name[20];int  age;char id[20];
};
void main() {//s是局部变量struct Stu s1,s2;//创建结构体对象
}//2
struct Stu
{   //结构的成员变量char name[20];int  age;char id[20];
}s1,s2;//s1,s2也是结构体变量,其是全局变量(创建结构体类型时顺带创建2个结构体全局变量)//3
//匿名结构体类型
struct {char name[20];int price;char id[12];
}s;

结构体自引用

定义:结构体要能够找到和他同类型的下一个结构体

结构体中本身可以包含另一个结构体变量

struct Book{struct Paper p;char name[20];int price;char id[12];
};
struct Paper {int size;char color[20];
};

但是结构体内不能有自身结构体变量,但是可以用指针指向一个本类型结构体来实现结构体自引用

//结构体自引用
struct Node
{int data;//数据域struct Node* next;//指针域
};

关于typedef(定义类型)定义定义匿名结构体类型

typedef struct
{int data;//数据域struct Node* next;//指针域
} Node;
void main() {Node n;}//编译后不可行

结构体的定义和初始化

struct People
{char c;short s;double d;
};
struct Stu
{   //结构的成员变量struct People p;char name[20];int  age;char id[20];
}s1 = { {'b',10,3.8},"lili",3,"8" },s2 = { {'j',20,3.1},"kate",302,"999" };//s1,s2也是结构体变量,其是全局变量,并对其全部初始化,其实也可不初始化
void main() {//s是局部变量struct Stu s = { {'c',20,3.14},"张三",30,"888"};//创建结构体对象并完成初始化
}//2.匿名结构体类型初始化
struct {char name[20];int price;char id[12];
}s = { "git",7,"123" };

注意:

  • 结构体的声明和初始化也可以在函数里面进行
  • 结构体定义完就必须对其进行初始化
  • 结构体也可以通过操作符"."的形式来进行里面值的操作

结构体成员的访问

结构体变量访问成员结构体变量通过操作符"."来访问的,"."操作符接受2个操作数eg:

#include <stdio.h>
struct People
{char c;short s;double d;
};
struct Stu
{   //结构的成员变量struct People p;char name[20];int  age;char id[20];
}s1 = { {'b',10,3.8},"lili",3,"8" },s2 = { {'j',20,3.1},"kate",302,"999" };//s1,s2也是结构体变量,其是全局变量,并对其全部初始化,其实也可不初始化
void main() {//s是局部变量struct Stu s = { {'c',20,3.14},"张三",30,"888"};//创建结构体对象并完成初始化//.操作符printf("%s\n", s.name);printf("%c\n", s1.p.c);//->操作符struct Stu* ps = &s2;printf("%c\n", (*ps).p.c);//因为.的优先级比*高,所以应加()printf("%d\n", ps->p.s);printf("%s\n", ps->name);
}

结构体内存对齐

定义:结构体成员在内存中到底如何存储,有一个存放的规则,这就是结构体的内存对齐规则。

结构体的对齐规则

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

理解:

  • 结构体的第一个成员放在结构体变量在内存中储存位置的0偏移地址处(选择0地址)
  • 从第二个成员往后的所有成员都放在一个对齐数(成员大小和编译器默认对齐数的较小值)的整数倍的地址处
  • 结构体的总大小是结构体所有成员对齐数中最大的那个对齐数的整数倍
  • 嵌套结构体则内层结构体对齐到自己内层最大对齐数的整数倍处,外层结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
#include <stdio.h>
struct People
{char c;short s;double d;
};
void main() {struct People p = { 0 };//不完全初始化——把第一个初始化成0,剩下的默认本就是0;printf("%d\n", sizeof(p));//16printf("%d\n", sizeof(struct People));//16
}
#include <stdio.h>
struct Hand
{int size;char q;
};//8
struct People
{struct Hand h;char c;short s;double d;
};
void main() {struct People p = { 0 };//不完全初始化——把第一个初始化成0,剩下的默认本就是0;printf("%d\n", sizeof(p));//24printf("%d\n", sizeof(struct People));//24
}
//计算Hand结构体4+1=5,5不是4的整数倍,所以Hand结构体大小为8
//计算People结构体找到最大对齐数4的整数倍,因为放在第一所以不用找了8+1——10+2——16+8=24;

为什么要内存对齐

总结:结构体内存对齐是拿空间换时间的做法

在设计结构体时我们既要满足对齐,又要节省空间,那么我们应该

  1. 让占用小的成员尽量集中在一起
  2. 修改默认对齐数

#pragma pack(num)//num为设置的默认对齐数

#include <stdio.h>
//修改默认对齐数为2
#pragma pack(2)
struct Hand
{char c;int size;char q;
};
#pragma pack()//取消设置的默认对齐,还原默认
void main() {printf("%d\n", sizeof(struct Hand));//默认对齐数8时——12,默认对齐数2时——8
}

计算成员变量相对于结构体类型的偏移量

offsetof函数,使用时需引入头文件#include <stddef.h>

#include <stdio.h>
#include <stddef.h>
struct Hand
{char c;int size;char q;
};
void main() {printf("%d\n", offsetof(struct Hand,c));//0printf("%d\n", offsetof(struct Hand,size));//4printf("%d\n", offsetof(struct Hand,q));//8
}

结构体传参

#include <stdio.h>
struct Stu
{   //结构的成员变量char name[20];int  age;char id[20];
};
//传值调用
void print1(struct Stu t) {printf("%s,%d,%s\n", t.name, t.age, t.id);
}
//传址调用
void print2(const struct Stu* t) {printf("%s,%d,%s\n", t->name, t->age, t->id);
}
void main() {//s是局部变量struct Stu s = {"张三",30,"888"};//创建结构体对象并完成初始化print1(s);//传值调用,浪费空间,不可以改变结构体变量print2(&s);//传址调用,省空间,可以改变结构体变量
}

注意:

结论:结构体传参时,要传结构体的地址。

结构体传参中const的作用:使原结构体只可以进行读取操作不可以进行更改操作,以防止误操作

位段

位段的声明和结构体的声明是类似的,有2个不同

  • 位段的成员必须是int,unsigned int,或signed int(也可以char等属于整形家族的)。
  • 位段的成员名后边有一个冒号和一个数字

举例: 

struct A
{int _a : 2;//这里的int也可以改成char等整形类型下面改不改都可以int _b : 5;int _c : 10;int _d : 30;
};

位段的内存分配

#include <stdio.h>
struct A
{//因为是int类型所以先开辟4个字节——32个比特位int a : 2; //a成员占2个比特位int b : 5; //b成员占5个比特位int c : 10;//c成员占10个比特位//用掉了17bit剩下15bit不够后面的30bit(因此,15bit直接舍弃重新开辟空间使用)因为下面是int所以又开辟32bitint d : 30;//d成员占30个比特位//总共用64bit因此占用空间8字节
};
void main() {printf("%d\n", sizeof(struct A));//8
}

注意:位段的大小根据需求设置,但位段大小不能设置超过类型所占空间的大小。

关于位段类型我的理解(VS编译器)

#include <stdio.h>
struct A
{char a : 4; char b : 5;char c : 7;char d : 6;
};
void main() {printf("%d\n", sizeof(struct A));//4//通过结果得知总共开辟了4次空间,说明开辟空间用后余下的空间没有和新开辟的空间结合,而是直接浪费掉
}
#include <stdio.h>
struct A
{int a : 4; char b : 5;char c : 2;char d : 6;};
void main() {printf("%d\n", sizeof(struct A));//8//通过结果得知int用后余下的空间虽然可以满足char的所有成员,但是没用,说明转换类型时,前类型余下的空间直接浪费掉
}

结构体8大小的由来

分配4字节空间给与a成员变量(结构体偏移量为0的地址处),转换类型,分配1字节空间给予以下char变量b和c(必须注意对齐到对齐数的整数倍地址处,因为是char所以就不纠结了)因为不够给d直接浪费掉,再开辟1字节空间给d(必须注意对齐到对齐数的整数倍地址处,因为是char所以就不纠结了),由于结构体大小是所有成员对齐数中最大那个对齐数的整数倍,也就是int(4字节)的整数倍,最后得出8

位段:变量类型所占空间的合理规划

在VS编译器下一个字节内部的数据,先使用低比特位的地址,再使用高比特位的地址(在内存中分配从右往左使用)

位段的跨平台问题

总结:和结构相比,位段可达到同样的效果,但是可以很好的节省空间,但是有跨平台问题的存在。

C语言之结构体及位段相关推荐

  1. C语言:结构体与位段——自定义类型

    目录 1.结构体 1.1结构的基础知识 1.2结构的声明 1.3特殊的声明 1.4结构的自引用 1.5结构体变量的定义和初始化 1.6结构体内存对齐 1.7检验结构体成员偏移量函数--offsetof ...

  2. 解析C语言结构体及位段

    一.结构体是C和C++用户自己来定义的一种数据类型. 1.结构体变量的定义方法由三种: (1)先声明结构体类型再定义结构体变量: 在C语言中,定义结构体变量要在结构体类型名前加关键字struct,而C ...

  3. 详解C语言自定义类型(结构体,位段,枚举,联合)

    C语言中有许多类型,比如整形int,字符型char,双精度浮点型double等等.这些类型可以存放一些值或者字符.但是如果我想要一种类型存放一本书,显然是没有的,那么这时候就需要自定义类型了,也就是结 ...

  4. 自定义类型:结构体、位段、枚举、联合 ------- C语言

    C语言中,7可以存放再整型变量中,'c' 可以存放在字符型变量中,3.14可以存放在浮点型变量中,一个学生有:姓名.年龄.性别.学号等.C语言中是否有这样一个可以存放学生的类型呢?答案是肯定的,这一种 ...

  5. C 语言编程 — 结构体的数据类型转换

    目录 文章目录 目录 数组类型强制类型转换为结构体 结构体之间的强制类型转换 数组类型强制类型转换为结构体 先看一个例子: #include <stdio.h>int main(void) ...

  6. 【C 语言】结构体 ( 结构体 数组 作为函数参数 | 数组 在 堆内存创建 )

    文章目录 一.结构体 数组 作为函数参数 ( 数组 在 堆内存创建 ) 二.完整代码示例 一.结构体 数组 作为函数参数 ( 数组 在 堆内存创建 ) 在上一篇博客 [C 语言]结构体 ( 结构体 数 ...

  7. 【剑仙教程】易语言的结构体。自定义数据类型。

    1楼. [剑仙教程]易语言的结构体.自定义数据类型. 在易语言中,有基本数据类型,自定义数据类型. 自定义数据类型,在C语言/C++中称为结构体.这是面向对象和类的编程. . . 先上代码.打开易语言 ...

  8. C 语言中结构体强制转换--实验

    2019独角兽企业重金招聘Python工程师标准>>> 对于C语言中结构体强制转换做了如下实验, 或许可以解惑一些问题 对于结构体, 我理解的属性有: 成员的顺序, 成员的类型,成员 ...

  9. java接收c语言的结构体

    java接收c语言的结构体 DataInputStream inputStream = new DataInputStream(s.getInputStream()); int read = 0; i ...

最新文章

  1. 服务器间无密码scp
  2. Android性能优化之启动优化实战篇,最新整理
  3. popoupwindow 点击背景消失_欺骗13亿人 国产剧的“假房子”什么时候消失?
  4. 表格行内编辑增删改查
  5. 安卓如何调出软键盘_智能汽车到底如何交互?小鹏用全场景语音给出了答案
  6. 计算机模拟技术在教学上的应用,计算机模拟技术在水利工程学科试验教学中的应用...
  7. ZooKeeper(二)ZooKeeper能做什么?
  8. error: Please reinstall the libcurl distribution - easy.h should be in curl-dir/include/curl/
  9. python metaclass应用
  10. ​ [RHEL7.1]重新封装系统(制作模板)
  11. [SPM_hw1]记一次项目经历
  12. 数学建模算法总结(一)
  13. Spring Boot 2.X默认连接池HikariCP详解
  14. Linux下用ffmpeg轉PSP影片 (MP4/AVC格式)
  15. URl和URI 通俗的例子
  16. mysql语句监控工具
  17. 广东大学计算机基础教材,21世纪高等学校计算机公共基础课规划教材:大学计算机基础(第2版)...
  18. 二十九、非谓语动词_作定语
  19. c# formApp的web browser的兼容性设置
  20. 使html轮廓颜色不同,css中border颜色不同怎么设置?

热门文章

  1. vue-amap实现实现初始化并定位当前位置,搜索,定位,增加点标记
  2. Python 卡尔曼滤波器实现
  3. matlab多元决策问题,matlab 贝叶斯决策对二维二分类问题的实现
  4. LSM树——Log-Structured Merge-Tree数据结构、LSM树设计思想、LSM的数据写入操作、LSM的数据查询操作
  5. 基于PreSCAN Matlab/Simulink的智能驾驶联合仿真【详细图文】
  6. 第一章 MUD:创造世界的巫师
  7. oracle复合数据类型
  8. 计算机文件管理word,WORD打开文件的方式和管理文件-word技巧-电脑技巧收藏家
  9. Incorrect column count: expected 1, actual 5
  10. 镁光139 8510