深圳企业网站建设报价,可以做申论的网站,网站 不 备案,flash网站规划C语言程序设计笔记---015 C语言数据的存储1、数据类型的意义1.1、unsigned与signed数据类型例程11.2、补码与原码相互转换例程2 2、大小端的介绍2.1、大小端的例程12.2、大小端的例程2 --- 判断当前编译器环境属于大端或小端 3、综合练习题探究数据的存储3.1、练习题13.2、练习… C语言程序设计笔记---015 C语言数据的存储1、数据类型的意义1.1、unsigned与signed数据类型例程11.2、补码与原码相互转换例程2 2、大小端的介绍2.1、大小端的例程12.2、大小端的例程2 --- 判断当前编译器环境属于大端或小端 3、综合练习题探究数据的存储3.1、练习题13.2、练习题23.3、练习题33.4、练习题43.5、练习题53.6、练习题63.7、练习题7 4、浮点数在内存中的存储4.1、浮点数存储规则4.1.1、IEEE(电气电子工程师协会) 拟定的754标准4.1.2、另外IEE电气电子工程师协会754对有效数字M和指数E,还有一些特定的规定 4.2、指数E的规定4.2.1、指数E的存入规定4.2.2、指数E的取出规定三种情况(1).E不全为0或不全为1(2).E全为0(3).E全为1 5、经典例子详解(1)、**以整型的形式存储以浮点数的取出**(2)、**以浮点数型的形式存储以整型的取出** 6、结语 C语言数据的存储
前言 常见数据类型回顾 char short int long long long float double 那么有没有字符串类型呢 ---- 没有 字符在内存中存储的是字符的ASCLL码值所以字符类型归于整型家族 /知识点汇总/
1、数据类型的意义
使用对应类型内存空间的大小大小决定了适用范围
unsigned ----- 无符号位 signed ----- 有符号位 值得注意的是。char — 默认为unsigned 还是 signed 由编译器决定
计算机能够处理的是二进制数据 整型和浮点型数据在内存中也是以二进制的形式进行存储的
整型的二进制表示形式有三种原码、补码、反码 正整数原码、反码、补码相同 负整数原码、反码、补码需要计算补码等于原码取反加1
最后不管是正整数还是负整数在内存中的存储都是补码的二进制序列
1.1、unsigned与signed数据类型例程1
#include stdio.h
int main()
{int a -10;//4个字节 --- 32bit位//1000 0000 0000 0000 0000 0000 0000 1010 ----- -10原码//1111 1111 1111 1111 1111 1111 1111 0101 ----- -10反码//1111 1111 1111 1111 1111 1111 1111 0110 ----- -10补码//最高位是符号位1负0正unsigned int b -10;//1111 1111 1111 1111 1111 1111 1111 0110//此时的b,被unsigned int 修饰此时的最高位就不作为符号位了return 0;
}小结 对于整形数据存放内存中其实放的是补码 为什么呢 因为在计算机系统中数值一律用补码形式来表示和存储。原因在于使用补码可以将符号位与数据位统一处理。 同时加法和减法也可以统一处理CPU只有加法器此外补码与原码相互转换其运算过程是相同的不需要额外的电路
1.2、补码与原码相互转换例程2
#include stdio.h
int main()
{//1-1//1(-1)//0000 0000 0000 0000 0000 0000 0000 0001 --- 1原码//1000 0000 0000 0000 0000 0000 0000 0001 --- -1原码//当用原码计算时//1000 0000 0000 0000 0000 0000 0000 0010 --- 原码1-1发现达不到想要的结果//所以再来常识补码相加//0000 0000 0000 0000 0000 0000 0000 0001 --- 1原码、反码、、补码相等//1000 0000 0000 0000 0000 0000 0000 0001 --- -1原码//1111 1111 1111 1111 1111 1111 1111 1110 --- -1反码//1111 1111 1111 1111 1111 1111 1111 1111 --- -1补码//补码相加//0000 0000 0000 0000 0000 0000 0000 0001 --- 1补码//1111 1111 1111 1111 1111 1111 1111 1111 --- -1补码//1 0000 0000 0000 0000 0000 0000 0000 0000 --- 保留后面32位bit --- 0 return 0;
}2、大小端的介绍
大端 — 大端字节序存储 把一个数据的低位字节处的数据存放在内存的高位地址处把一个数据的高位字节处的数据存放在内存的低位地址处。 小端 — 小端字节序存储 把一个数据的低位字节处的数据存放在内存的低位地址处把一个数据的高位字节处的数据存放在内存的高位地址处。
2.1、大小端的例程1
#include stdio.h
int main()
{int a 0x11223344;//大小端的不同存放的顺序就不同//11 22 33 44 --- 大端存储//44 33 22 11 --- 小端存储printf(%p\n,a);//当前VS2019属于小端存储return 0;
}2.2、大小端的例程2 — 判断当前编译器环境属于大端或小端
#include stdio.h
int check_sys()
{int a 1;//只需判断当前属于小端还是大端所以赋值变量a1,判断内存中的01的位置即可//char* p (char*)a;//判断只需要一个字节第一个字节即可if (*p 1){ return 1;}else return 0;//return *p;return *(char*)a;
}
int main()
{if (1 check_sys()){printf(小端\n);}else{printf(大端\n);}return 0;
}3、综合练习题探究数据的存储
3.1、练习题1
#include stdio.h
int main()
{char a -1;//1000 0000 0000 0000 0000 0000 0000 0001 --- -1原码//1111 1111 1111 1111 1111 1111 1111 1110 //1111 1111 1111 1111 1111 1111 1111 1111 ---- -1补码//1111 1111 ---- char -1//因为要以%d格式打印需要整型提升且以原码打印//整型提升以符号位提升//1111 1111 1111 1111 1111 1111 1111 1111 --- 整型提升此时任然为补码//1000 0000 0000 0000 0000 0000 0000 0000//1000 0000 0000 0000 0000 0000 0000 0001 --- 原码 补码取反加1//以%d打印 --- -1signed char b -1;//1111 1111 1111 1111 1111 1111 1111 1111 ---- -1补码//1111 1111 ---- char -1补码//整型提升以符号位提升//1111 1111 1111 1111 1111 1111 1111 1111 --- 整型提升此时任然为补码//1000 0000 0000 0000 0000 0000 0000 0000//1000 0000 0000 0000 0000 0000 0000 0001 --- 原码 补码取反加1//以%d打印 --- -1unsigned char c -1;//1111 1111 1111 1111 1111 1111 1111 1111 ---- -1补码//1111 1111 ---- char -1补码 ---- %d ---255//整型提升无符号位提升补0//0000 0000 0000 0000 0000 0000 1111 1111 --- 整型提升此时任然为补码//无符号判定为正整数原码 反码 补码//以%d打印 --- 255printf(a %d,b %d,c %d,a,b,c);//整形数据以补码存储//%d 是以10进制得形式打印有符号的整型数据以原码打印return 0;
}3.2、练习题2
#include stdio.h
int main()
{char a -128;//1000 0000 0000 0000 0000 0000 1000 0000 --- -128//1111 1111 1111 1111 1111 1111 0111 1111 //1111 1111 1111 1111 1111 1111 1000 0000 --- -128补码//数据截断//1000 0000 --- char -128补码//整型提升//1111 1111 1111 1111 1111 1111 1000 0000 -128补码//以%u打印无符号数所以//1111 1111 1111 1111 1111 1111 1000 0000 ----4294967168 原码 补码 反码printf(%u\n,a);//4294967168return 0;
}3.3、练习题3
#include stdio.h
int main()
{char a 128;//0000 0000 0000 0000 0000 0000 1000 0000 --- 128 原码//1111 1111 1111 1111 1111 1111 0111 1111 ---- 反码//1111 1111 1111 1111 1111 1111 1000 0000 --- -128补码//数据截断//1000 0000 --- char -128补码//整型提升//1111 1111 1111 1111 1111 1111 1000 0000 补码//以%u打印无符号数所以//1111 1111 1111 1111 1111 1111 1000 0000 ----4294967168 原码 补码 反码printf(%u\n,a);//4294967168return 0;
}小结 char类型数据范围-128~127 char – 假设是有符号的char — signed char 范围就是-128~127 单位字节1个字节 8个bit //0000 0000 ---- 0 //0000 0001 ---- 1 //0000 0010 ---- 2 //… … //0111 1111 ---- 127 //1000 0000 ---- -128 //10000001 ---- -127 //1000 0010 ---- -126 //… … //1111 1111 ---- -1 如图所示
注意 首位依然代表符号位 内存中存的二进制序列 内存中存储的补码
char – 假设是无符号的char — usigned char 0~255 同理都会数据截断将保存正确的字节
3.4、练习题4
#include stdio.h
int main()
{int i -20;//1000 0000 0000 0000 0000 0000 0001 0100 ---- -20原码//1111 1111 1111 1111 1111 1111 1110 1011 ---- -20反码//1111 1111 1111 1111 1111 1111 1110 1100 ---- -20补码因为整形数据以补码二进制序列保存unsigned int j 10;//0000 0000 0000 0000 0000 0000 0000 1010 ---- 10原码、反码、补码相等printf(%d\n,ij);//-10//1111 1111 1111 1111 1111 1111 1110 1100 ---- -20补码//0000 0000 0000 0000 0000 0000 0000 1010 ---- 10原码、反码、补码相等//1111 1111 1111 1111 1111 1111 1111 0110 ---- ij 补码//以%d,打印一个有符号的整数//1000 0000 0000 0000 0000 0000 0000 1001 --- 补码取反//1000 0000 0000 0000 0000 0000 0000 1010 --- -10 补码取反1,以原码打印return 0;
}3.5、练习题5
#include stdio.h
int main()
{unsigned int i 0;//unsigned int范围0~255恒大于0与i 0恒成立for (i 9; i 0; i--){printf(%u\n,i);//死循环}return 0;
}3.6、练习题6
#include stdio.h
#include string.hint main()
{char a[1000];int i 0;for (i 0; i 1000; i){a[i] -1 - i;}printf(%d,strlen(a));//统计\0之前的个数//-1 -2 -3 -4 .... -128 127 126 125 ... 5 4 3 2 1 0结束 ---- 255个//因为char类型范围是-128 ~ 127所以其他数都会被数据截断以这个范围的数保存return 0;
}3.7、练习题7
#include stdio.hunsigned char i 0;//unsigned char范围0~255与i 255恒成立int main()
{for (i 0; i 255; i){printf(666\n);//死循环}return 0;
}综上所述 当扩展到其他的整型数据类型同理可得 short — 2个字节 — 16bit signed short范围-32678~32767 unsigned short范围0~65535
int — 4个字节 — 32bit signed int范围-2147483648~2147483647 unsigned int范围0~4294967295 … 补充为什么char 属于整型呢 字符在内存中存储的是字符的ASCLL码值所以字符类型归于整型家族
4、浮点数在内存中的存储
常见的浮点数类型 float double long double 说明由于浮点数的存储较为复杂先用一个典型的例子作为引入探究。
#include stdio.h
int main()
{int n 9;float* pFloat (float*)n;//以整型的形式存储以浮点数的取出printf(n的值为%d\n,n);//9printf(*pFloat值为%f\n,*pFloat);//0.000000//以浮点数型的形式存储以整型的取出*pFloat 9.0;printf(num的值为%d\n,n);//1091567616printf(* pFloat值为%f\n, *pFloat);//9.000000return 0;
}我们通过此代码的输出结果产生了与预想结果不同的输出那么为什么会是以这样的数据输出呢 那么我们就继续探讨一下为何会有如此的结果呢
4.1、浮点数存储规则
4.1.1、IEEE(电气电子工程师协会) 拟定的754标准
根据IEEE(电气电子工程师协会) 拟定的754标准任意一个二进制浮点数以V表示均可表示为一下形式 标准格式 (-1)^S * M * 2^E (-1)^S 表示符号位当S 0,V为正数当S 1V为负数 M表示有效数字范围大于等于1小于2 2^E表示指数位
浮点数标准格式V (-1)^S * M * 2^E
举个例子 5.5(十进制) 5.5二进制101.1 — 以权重计算以小数点分割小数点右边0.5 1/(2^1) 科学表示格式 (-1)^0 * 1.011 * 2^2 S 0 ; M 1.011 ; E 2 注意因为需要满足IEEE 754标准所以我们将M的小数点向左移动了两位使其满足 1M2 的标准范围其次指数位E也就得到了 2
再举个例子同理 9.0十进制 9.0二进制1001.0 科学计数法形式(-1)^0 * 1.001* 2^3 S 0; M 1.001 E 3
对于浮点数的数据存储规定 (1)、对于32位的浮点数最高的1位是符号位S接着的8位是指数位E剩下的23位是有效数字位M 即 1 8 23 (2)、对于64位的浮点数最高的1位是符号位S接着的11位是指数位E剩下的52位是有效数字位M 即 1 11 52
4.1.2、另外IEE电气电子工程师协会754对有效数字M和指数E,还有一些特定的规定
首先前面知道了M范围1M2也就是说可以写成1.XXXXXXX的形式其中的XXXXXXX表示小数部分 所以在IEEE 754中规定在计算机内部保存M时默认这个数的第一位是1那么因此可以将该位的1暂时舍去等读取时再默认自动添加上。 这样的目的就是节省1位有效数字提升效率意义在于使得浮点数的精度更高。
4.2、指数E的规定
4.2.1、指数E的存入规定
然后对于指数E的规定就更为复杂一些看看下面这个例子 0.5十进制 0.1二进制 科学计数法表示为(-1)^0 * 1.0* 2^(-1) S 0 M 1.0 E -1 通过这里发现与IEEE 754 规定的E为无符号数冲突此时出现了负数-1 那么如何通过刚才提到的另外的特殊规定解决呢
紧接着IEE电气电子工程师协会科学家提出了指数E存入的规定 首先当E为一个无符号整数unsigned int 这意味着如果E为8位(32位环境)它的取值范围为0-255如果E为11位(64位环境)它的取值范围为0~2047。 但是我们刚刚的例子知道科学计数法中的E是会出现负数的 **所以IEEE 754规定存入内存时E的真实值必须再加上一个中间数对于8位的E这个中间数是127对于11位的E这个中间数是1023。**这样进行一个特定的运算才能保留计算精度保证数据的完整性和真实性。 比如2 ^ 10的E是10所以保存成32位浮点数时必须保存成10 127 137即10001001。
#include stdio.h
int main()
{float f 5.5;//5.5十进制//101.1二进制//(-1)^0*1.011*2^2科学表达式//S 0; --- 1bit//M 1.011 --- 8bit//E 2 --- 23bit//存储二进制为在学过IEEE 754标准规定后可知 -- 32位环境S0 E2 127 -- 1000 0001 M011 (根据符号位补0)00000 00000 00000 00000//即0 1000 0001 011 0000 0000 0000 0000 0000//0100 0000 1011 0000 0000 0000 0000 0000//十六进制//0x 40 b0 00 00printf(%f\n,f);printf(%p\n,f);return 0;
}如图所示
当我们了解的E指数位数据的存入标准那么我们又是如何取出使用呢
4.2.2、指数E的取出规定三种情况
指数E从内存中取出规定还可以再分成三种情况 1.E不全为0或不全为1 2.E全为0 3.E全为1
(1).E不全为0或不全为1
这时浮点数就采用下面的规则表示即指数E的计算值减去127或1023得到真实值再将有效数字M前加上第一位的1。 比如 0.51/2的二进制形式为0.1由于规定正数部分必须为1即将小数点右移1位则为1.0*2^(-1)其阶码为-1127126表示为01111110而尾数1.0去掉整数部分为0补齐0到23位00000000000000000000000则其二进制表示形式为: 0 01111110 00000000000000000000000
(2).E全为0
这时浮点数的指数E等于 1-127或者1-1023即为真实值有效数字M不再加上第一位的1而是还原为0.xxxxxx的小数。 这样做是为了表示 ±0以及接近于0的很小的数字理解为无穷小。
(3).E全为1
这时如果有效数字M全为0表示 ±无穷大正负取决于符号位s
现在学习了以上知识点再回顾上面那道题
5、经典例子详解
(1)、以整型的形式存储以浮点数的取出
首先这里先定义了一个整型的正整数变量 n 9在上面知识点讲到正整数的原码。反码、补码相等。 所以我们代码中以%d格式打印 n的值不言而喻等于 9正常输出 然后我们看到代码中定义了一个浮点型的指针变量 pFloat 并令n的地址强转后赋值给它然后我们根据上面提到的IEEE(电气电子工程师协会) 拟定的754标准和规则知道了浮点数的存储方式。所以当n的地址强转为浮点型时立马通过n变量的二进制序列知道采用标准的科学计数法表示 0S 0000 0000(E) 00000000000000001001(M) S 0 E 0 M 0.00000000000000001001 由于E为全0的情况通过标准规定知道它的真实存储形式需要使E 1 - 127 -126且我们舍去的最高位的1不再还原回来。 最后我们将还原为真实的 pFloat 值以科学表示法表示为 (-1)^0 * 0.00000000000000001001* 2^(-126) 不难看出是一个巨小的数无限接近于0无穷小约等于0 所以当我们以%f的格式打印时结果自然而然就为0.000000
(2)、以浮点数型的形式存储以整型的取出
接下来我们继续探究后面的代码当我们将9.0一个浮点型的数据存入一个float* 变量 pFloat时通过上面的讲解不难理解以浮点型存入以%f格式打印就正常输出9.000000 那么重点讲解的是当以浮点型存入以%d格式输出时的情况 首先我们知道了IEE 754标准立马反应知道以科学表示格式表示所以 二进制转换大家都会就不多赘述十进制9.0转换为二进制得到 1001.0再以科学计数法表示为 (-1)^0 * 1.001* 2^3 S 0E 3M 1.001 又因为当前编译器环境是32位所以执行 1 8 23标准见上文 0(S) 1000 0010(E) 001 000000000000000000000(M补01暂时舍去) 即0 1000 0010 001 000000000000000000000 以二进制表示 0100 0001 0001 0000 0000 0000 0000 0000 以十六进制表示 0x41 10 00 00 以十进制%d打印表示 1091567616
如图所示 代码部分
#include stdio.h
int main()
{int n 9;//0000 0000 0000 0000 0000 0000 0000 1001 --- 9原码、反码、补码相等float* pFloat (float*)n;//当编译器识别此时为float类型时就自动识别为//0 0000 0000 00000000000000001001//S 0//E 0 ---全0的情况,E 1 -127 -126 且M不再添加舍去的最高位1//M 0.00000000000000001001//科学表示法//(-1)^0*0.00000000000000001001*2^(-126) ---- 接近于无穷小得约等于0//以整型的形式存储以浮点数的取出printf(n的值为%d\n,n);//9printf(*pFloat值为%f\n,*pFloat);//0.000000//以浮点数型的形式存储以整型的取出*pFloat 9.0;//9.0//1001.0//(-1)^0*1.001*2^3//S 0//M 1.001 //E 3 3127---- 1000 0010//二进制序列//0 1000 0010 001 000000000000000000000//0100 0001 0001 0000 0000 0000 0000 0000 --- float形式存入且原码、反码、补码相同printf(num的值为%d\n,n);//以%d形式打印1091567616printf(* pFloat值为%f\n, *pFloat);//9.000000return 0;
}当前编译器以小端存储方式验证结果如图所示 6、结语
半亩方糖一鉴开天光云影共徘徊。 问渠哪得清如许?为有源头活水来。–朱熹观书有感