在这段没有更新的时间作者大一开学了,军训期间一直比较忙没时间学习,9月23号结束了为期十四天的军训,今天开始重新更学习C语言的博客
整理今天的学习内容
1.浮点数在内存中的储存
浮点数包括float,double,long double 类型,浮点数表示的范围在float.h文件中定义
(1)浮点数的表示方法
根据国际标准IEEE(电气和电子工程协会)754,任意⼀个二进制浮点数V可以表示成下面的形式:
V = (−1) ^S* M *2^E
(−1)^S 表示符号位,当S=0,V为正数;当S=1,V为负数
M表示有效数字,M是大于等于1,小于2的
2^E 表示指数位
例:5.5的表示形式为:(-1)^ 0 * 1.011 * 2 ^ 2
↑ ↑ ↑
S M E
(2)浮点数存的过程
IEEE754规定:
对于32位的浮点数,最高的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M对于64位的浮点数,最高的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M
·
对于M:
由于1≤M<2 ,所以M可以写成 1.xxxxxx 的形式,其中 xxxxxx 表示小数部分。IEEE754规定,在计算机内部保存M时,默认这个数的第⼀位总是1,因此可以被舍去,只保存后面的xxxxxx部分,这样做的目的是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,相当于可以保存24位有效数字
对于E:
E为⼀个无符号整数,如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0~2047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE754规定,存入内存时E的真实值必须再加上⼀个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023
(3)浮点数取的过程
E不全为0或不全为1:
指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第⼀位的1
E全为0:
浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第⼀位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字
E全为1:
如果有效数字M全为0,表示±无穷大(正负取决于符号位s)
(4)解释下面程序的运行结果
n在内存中的储存为00000000 00000000 00000000 00001001
以浮点数的形式打印,则S为0,E为00000000,M为00000000000000000001001
由于E为全0,所以E为1-127,V=(-1)^0×0.00000000000000000001001×2^(-126)=1.001×2^(-146),用十进制表示是0.000000
*pFloat浮点数的二进制表示为V=(-1)^0*1.001*2^3,在内存中的储存为0 10000010 00100000 000000000000000
以整数的形式打印,补码为01000001 00010000 00000000 00000000 ,原码同,以十进制的形式打印,为1091567616
自定义类型:结构体
1.结构的特殊声明(不完全声明)
如:
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20],*p;
上面两个结构在声明的时候省略掉了结构体标签
在上面代码的基础上,p=&x;是不合理的
编译器会把上面的两个声明当成完全不同的两个类型,所以是非法的,匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用⼀次
2.结构的自引用
(1)自引用
例:(定义一个链表的节点)
错误的自引用方式:
struct Node
{
int data;
struct Node next;
}
这样结构体变量的大小会无穷大,不合理
正确的自引用方式:
struct Node
{
int data;
struct Node*next;//存放下一个节点的地址
}
(2)自引用中typedf重命名的问题
错误示范:typedef struct
{
int data;
Node*next;
}Node;
typedef 把结构体重命名为了Node,但在重命名前Node就被使用,编译器认为Node是未定义类型,解决方法:定义结构体不要使用匿名结构体,在内部自引用时用原来的名字
3.结构体内存对齐
(1)对齐规则
1. 结构体的第⼀个成员对齐到和结构体变量起始位置偏移量为0的地址处
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
对齐数 = 编译器默认的⼀个对齐数 与 该成员变量大小的较小值
VS 中默认的值为 8
Linux中 gcc 没有默认对齐数,对齐数就是成员自身的大小
3. 结构体总大小为最大对齐数(结构体中每个成员变量都有⼀个对齐数,所有对齐数中最大的)的
整数倍
4. 如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构
体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍
(2)解释下面程序的运行结果
根据规则:
根据规则:
(3)存在内存对齐的原因
平台原因 (移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常
性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。假设一个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用⼀个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中
总体来说:结构体的内存对齐是拿空间来换取时间的做法
在设计结构体时,为了既满足对齐又节省空间,应该让占用空间小的成员尽量集中在一起
(4)修改默认对齐数
结构体在对齐方式不合适的时候,我们可以自己更改默认对齐数
#pragma 这个预处理指令,可以改变编译器的默认对齐数
#pragma pack(1)//设置默认对齐数为1
#pragma pack( )//取消设置的对齐数,还原为默认
4.结构体传参
函数传参的时候,参数需要压栈,会有时间和空间上的系统开销。
如果传递⼀个结构体对象的时候结构体过大,参数压栈的的系统开销就会比较大,所以会导致性能的下降。所以结构体传参的时候,最好传结构体的地址。