征服C语言之数据的存储

征服C语言之数据的存储,第1张

征服C语言之数据的存储

目录

一.数据类型的介绍

类型的基本归类

 整形在内存中的存储

 大小端的介绍

char类型的取值范围

练习

补充一个小知识点

 三.浮点型在内存中的存储

一个例子

 ​

浮点数在内存中的存储


一.数据类型的介绍 前面我们已经学习了基本的内置类型:
char         // 字符数据类型 short       // 短整型 int         // 整形 long         // 长整型 long long   // 更长的整形 float       // 单精度浮点数 double       // 双精度浮点数 //C 语言有没有字符串类型?
以及他们所占存储空间的大小。 类型的意义: 1. 使用这个类型开辟内存空间的大小(大小决定了使用范围)。 2. 如何看待内存空间的视角 类型的基本归类

 整形家族:

char         unsigned char         signed char short         unsigned short [ int ]         signed short [ int ] int         unsigned int         signed int long         unsigned long [ int ]         signed long [ int ]

浮点数家族:

float double

 构造类型:

> 数组类型 > 结构体类型 struct > 枚举类型 enum > 联合类型 union

 指针类型:

int *pi;
char *pc;
float* pf;
void* pv;

空类型:
void 表示空类型(无类型) 通常应用于函数的返回类型、函数的参数、指针类型。
 整形在内存中的存储 计算机中的整数有三种表示方法,即原码、反码和补码。 三种表示方法均有 符号位 和 数值位 两部分,符号位都是用 0 表示 “ 正 ” ,用 1 表示 “ 负 ” ,而数值位
#define _CRT_SECURE_NO_WARNINGS
#include
int main()
{
	int a = -10;
	int b = 10;
	return 0;
}

对于b变量来说,原反补都相同

二级制:00000000000000000000000000001010

十六进制:0000000a

对于a变量来说,

按照数据的数值直接写出的二进制序列就是原码

原码的符号位不变,其他位按位取反,得到的就是反码

反码+1,得到的就是补码

原码:1000 0000 0000 0000 0000 0000 0000 1010反码:1111 1111 1111 1111 1111 1111 1111 0101补码:1111 1111 1111 1111 1111 1111 1111 0110

对于整形来说:数据存放内存中其实存放的是补码。

 为什么呢?

在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理; 同时,加法和减法也可以统一处理( CPU 只有加法器 )此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

 

我们可以看到对于 a 和 b 分别存储的是补码。但是我们发现顺序有点 不对劲 。 这是又为什么?  大小端的介绍

 为什么会有大小端

这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8 bit 。但是在 C 语言中除了 8 bit 的 char 之外,还有 16 bit 的 short 型, 32 bit 的 long 型(要看具体的编 译器)
另外,对于位数大于 8 位 的处理器,例如 16 位或者 32 位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。
#define _CRT_SECURE_NO_WARNINGS
#include
int main()
{
	int a = 0x11223344;
	return 0;
}


百度2015年系统工程师笔试题:

请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序  

 看代码:

#define _CRT_SECURE_NO_WARNINGS
#include
int check_sys()
{
	int a = 1;//0x00000001
	char* p = (char*)&a;//int*
	return *p;//返回1表示小端,返回0表示大端
}

int main()
{
	//写代码判断当前机器的字节序
	int ret = check_sys();
	if (ret == 1)
	{
		printf("小端n");
	}
	else
	{
		printf("大端n");
	}

	return 0;
}

对于1来说,在小端存储中0x01000000,在大端存储中0x00000001所以只需要判断第一个字节是不是1就能判断机器的字节序因为char*能一个字节一个字节的访问,所以就有了char* p = (char*)&a; char类型的取值范围

 有符号的char取值范围是-128到127,无符号的char与这个类似

练习

第一题:

变量a和b同理,虽然C语言没有明确指出char是不是有符号的,但是在这里的char是有符号的

且发生整形提升都是补原符号位 

对于变量C来说:它是无符号的,整形提升补0

 三个变量都以%d的形式打印:

其中变量a和b符号位是1,这两个是负数,所以需要-1,按位取反,得到:10000000000000000000000000000001,-1变量c的符号位是0,或者说是无符号位的,是正数,所以它的原反补相同
00000000000000000000000011111111,255


 第二题

 

变量a最后是以%u的形式打印的,所以认为整形提升之后得到的是一个无符号位的,还是
11111111111111111111111110000000,4294967168


第三题

 

 变量a最后是以%u的形式打印的,所以认为整形提升之后得到的是一个无符号位的,还是
11111111111111111111111110000000,4294967168


第四题

 i+j是以%d打印,所以是有符号的,如果是以%u打印的话,结果又会不同


 第五题

 结果会死循环因为变量i的类型是unsigned int,这个类型恒>=0,所以判断条件恒为真,所以死循环

 第六题

 strlen读取到结束,并计算的是之前的长度,负数有128,正数有127,一共255


 第七题

  •  此代码,结果死循环,
  • 因为变量i的类型是unsigned char,这种类型的最大值是255,所以for循环的判断条件,又是恒成立的,导致最后死循环 补充一个小知识点

    用下面这种方法可以查看,int的取值范围,其他的方法类似

     limits.h是放一些整形类型取值范围的头文件如果要查看浮点型取值范围,应该包含float.h  三.浮点型在内存中的存储 常见的浮点数:

    3.14159 1E10 浮点数家族包括: float 、 double 、 long double 类型。 浮点数表示的范围: float.h 中定义
    一个例子
    #define _CRT_SECURE_NO_WARNINGS
    #include 
    int main()
    {
    	int n = 9;
    
    	float* pFloat = (float*)&n;
    	printf("n的值为:%dn", n);
    	printf("*pFloat的值为:%fn", *pFloat);
    
    	*pFloat = 9.0;
    	printf("num的值为:%dn", n);
    	printf("*pFloat的值为:%fn", *pFloat);
    
    	return 0;
    }
     

     结果是不是出乎你的预料? 浮点数在内存中的存储 num 和 *pFloat 在内存中明明是同一个数,为什么浮点数和整数的解读结果会差别这么大? 要理解这个结果,一定要搞懂浮点数在计算机内部的表示方法。 详细解读: 根据国际标准 IEEE (电气和电子工程协会) 754 ,任意一个二进制浮点数 V 可以表示成下面的形式:

    (-1)^S * M * 2^E (-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。 M表示有效数字,大于等于1,小于2。 2^E表示指数位。

    举例来说: 十进制的 5.0 ,写成二进制是 101.0 ,相当于 1.01×2^2 。 那么,按照上面 V 的格式,可以得出 s=0 , M=1.01 , E=2 。 十进制的 -5.0 ,写成二进制是 - 101.0 ,相当于 - 1.01×2^2 。那么, s=1 , M=1.01 , E=2 。 IEEE 754 规定: 对于 32 位的浮点数,最高的 1 位是符号位 s ,接着的 8 位是指数 E ,剩下的 23 位为有效数字 M 。 对于 64 位的浮点数,最高的 1 位是符号位S,接着的 11 位是指数 E ,剩下的 52 位为有效数字 M 。

    IEEE 754 对有效数字 M 和指数 E ,还有一些特别规定。 前面说过, 1≤M<2 ,也就是说, M 可以写成 1.xxxxxx 的形式,其中 xxxxxx 表示小数部分。 IEEE 754 规定,在计算机内部保存 M 时,默认这个数的第一位总是 1 ,因此可以被舍去,只保存后面的 xxxxxx 部分。比如保存 1.01 的时 候,只保存 01 ,等到读取的时候,再把第一位的 1 加上去。这样做的目的,是节省 1 位有效数字。以 32 位浮点数为例,留给M 只有 23 位, 将第一位的 1 舍去以后,等于可以保存 24 位有效数字。 至于指数 E ,情况就比较复杂。

    首先,E为一个无符号整数(unsigned int)

    这意味着,如果 E 为 8 位,它的取值范围为 0~255 ;如果 E 为 11 位,它的取值范围为 0~2047 。但是,我们知道,科学计数法中的E 是可以出现负数的,所以IEEE 754 规定,存入内存时 E 的真实值必须再加上一个中间数,
    对于8位的E,这个中间数是127;
    对于11位的E,这个中间数是1023。 比如, 2^10 的 E 是 10 ,所以保存成 32 位浮点数时,必须保存成 10+127=137 ,即10001001。 然后,指数 E 从内存中取出还可以再分成三种情况:

     E不全为0或不全为1

    这时,浮点数就采用下面的规则表示,即指数 E 的计算值减去 127 (或 1023 ),得到真实值,再将 有效数字 M 前加上第一位的 1 。 比如: 0.5 ( 1/2 )的二进制形式为 0.1 ,由于规定正数部分必须为 1 ,即将小数点右移 1 位,则为 1.0*2^(-1) ,其阶码为 -1+127=126 ,表示为 01111110 ,而尾数 1.0 去掉整数部分为 0 ,补齐 0 到 23 位 00000000000000000000000 ,则其二进制表示形式为:

     

    E全为0
    这时,浮点数的指数 E 等于 1-127 (或者 1-1023 )即为真实值, 有效数字 M 不再加上第一位的 1 ,而是还原为 0.xxxxxx 的小数。这样做是为了表示 ±0 ,以及接近于0的很小的数字。

     E全为1

    这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s);

    好了,关于浮点数的表示规则,就说到这里。


     看代码

    #define _CRT_SECURE_NO_WARNINGS
    #include
    int main()
    {
    	float f = 5.5f;
    	return 0;
    }


     好了我们再来分析刚才的这段代码

    #define _CRT_SECURE_NO_WARNINGS 1
    #include 
    int main()
    {
    	int n = 9;
    
    	float* pFloat = (float*)&n;
    	printf("n的值为:%dn", n);
    	printf("*pFloat的值为:%fn", *pFloat);
    
    	*pFloat = 9.0;
    	printf("num的值为:%dn", n);
    	printf("*pFloat的值为:%fn", *pFloat);
    
    	return 0;
    }

    对于第一个printf,毫无疑问结果是9,相信大家都能理解,对于第二个printf,float* pFloat = (float*)&n;它将n的地址强制转化成float*,并赋给了pFloat,pFloat指向9并解引用,最后又是以%f打印的,所以结果为0.000000
     对于第三个printf,*pFloat = 9.0;把9的值赋给了n,且pFloat是一个float* 的指针变量,最后又是以%d的形式打印,所以结果为1091567616
    对于第四个printf,和第三个printf同理,不同之处是
    第三个printf以浮点数存入,以%d的形式打印,
    第四个printf中也是以浮点数存入,但是却是以%f,
    所以结果应该为9.000000

    -------------------------------------------------------------------------------------------------------------------------------- 

    本章完

    欢迎分享,转载请注明来源:内存溢出

    原文地址: https://www.outofmemory.cn/zaji/5711850.html

  • (0)
    打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
    上一篇 2022-12-17
    下一篇 2022-12-17

    发表评论

    登录后才能评论

    评论列表(0条)

    保存