贵州公司网站开发网站设计标语

当前位置: 首页 > news >正文

贵州公司网站开发,网站设计标语,亚马逊网站域名,个人注册入口官网文章目录 一、前言二、类型的基本归类1、整型家族2、浮点数家族3、构造类型4、指针类型 三、整型在内存中的存储1、原码、反码、补码1.1 概念1.2 原码与补码的转换形式1.3 计算机内部的存储编码 2、大小端介绍2.1 为什么要有大端和小端之分#xff1f;2.2 大#xff08;小2.2 大小端字节序存储2.3 一道百度系统工程师笔试题 3、数据范围的介绍3.1 char与signed char数据范围3.2 unsigned char数据范围 4、笔试题 四、浮点型在内存中的存储1、引入2、浮点数存储规则2.1 概念 3、开局疑难解答 一、前言 前面我们已经学习了基本的内置类型以及他们所占存储空间的大小 类型的意义 使用这个类型开辟内存空间的大小大小决定了使用范围如何看待内存空间的视角 二、类型的基本归类 1、整型家族 下面是整形家族 char在字符存储的时候存的是一个ASCLL码值而ASCLL码值是一个整数 数值有正数和负数之分 有些数值只有正数没有负数身高—— unsigned有些数值有正数也有负数温度—— signed
2、浮点数家族 浮点数只分为两类一个是【float】一个是【double】 3、构造类型 4、指针类型 -下面我们来介绍一下这个空指针 void叫做【空指针】 对于int类型的指针可以用来接收int类型的数据的地址对于char类型的指针可以用来接收char类型的数据的地址对于float类型的指针可以用来接收float类型的数据的地址 对于void类型的指针可以用来接收任何类型数据的地址【它就像一个垃圾桶一样起到临时存放的作用】 void 表示空类型无类型 通常应用于函数的返回类型、函数的参数、指针类型 三、整型在内存中的存储 1、原码、反码、补码 1.1 概念 计算机中的整数有三种2进制表示方法即原码、反码和补码。 三种表示方法均有符号位和数值位两部分符号位都是用0表示“正”用1表示“负”而数值位正数的原、反、补码都相同负整数的三种表示方法各不相同 int a 10;对于正数说因为原、反、补都是相同的~~ int a -10;对于负数来说就不太一样了要得到反码就将原码除符号位外其余各位取反要得到补码的话就在反码的基础上 1 1.2 原码与补码的转换形式 原码到补码 —— 1种方式 原码取反1得到补码 补码到原码 —— 2种方式 补码 - 1取反得到原码补码取反1得到原码 1.3 计算机内部的存储编码 对于整形来说数据存放内存中存放的是补码。 这里存储的是16进制类型的补码在【进制转换】中4位二进制表示1位16进制。通过将补码4位4位进行一个划分就可以得出8个16进制的数字为ff ff ff f6这里发现是倒着存的那么就要涉及到大小端存储的 2、大小端介绍~~ 2.1 为什么要有大端和小端之分 在计算机系统中我们是以字节为单位的每个地址单元都对应着一个字节一个字节为8 bit 但是在C语言中除了8 bit的char之外还有16 bit的short 型32 bit的long型要看具体的编译器另外对于位数大于8位的处理器例如16位或者32 位的处理器由于寄存器宽度大于一个字节那么必然存在着一个如何将多个字节安排的问题。因 此就导致了大端存储模式和小端存储模式。例如一个 16bit 的 short 型 x 在内存中的地址为 0x0010 x 的值为 0x1122 那么 0x11 为 高字节 0x22 为低字节。对于大端模式就将 0x11 放在低地址中即 0x0010 中 0x22 放在高 地址中即 0x0011 中。小端模式刚好相反。我们常用的 X86 结构是小端模式而 KEIL C51 则 为大端模式。很多的ARMDSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式 还是小端模式。大端】和【小端】的由来接下去呢就正式地来给读者介绍一下这种倒着存放的方式 2.2 大小端字节序存储 【大端存储模式】是指数据的低位保存在内存的高地址中而数据的高位保存在内存的低地址中【小端存储模式】是指数据的低位保存在内存的低地址中而数据的高位,保存在内存的高地址中; 对于下面这一个十六进制数0x11223344以进制的权重来看的话右边的权重低【0】左边的权重高【3】所以11为高位44为低位。若是对其进行小端字节存储的话就要将44存放到低位11存放到高位 【大端字节序存储】是要将高位存放到低地址低位存放到高地址因此11要放在左边44要放在右边 2.3 一道百度系统工程师笔试题 请简述大端字节序和小端字节序的概念设计一个小程序来判断当前机器的字节序。10分 下面我通过一个简单的数作为案例来进行一个分析 int a 1;对于a以【小端字节序存储】会将01放在低位而以【大端字节序存储】会将01放在高位那么此时我们只需要获取到内存中规定最低位即可因为01在内存中表示一个字节而一个【char】类型的数据就为1个字节所以此时我们可以使用到一个字符型指针接受要存放到内存中的这个数然后对其进行一个解引用就可以获取到低位的第一个字节了 char p a;若直接使用一个字符型指针去接收一个整型数值的地址就会出现问题因为一个字符型的指针只能放得下一个字节的数据所以我们要对这个整型的数值去进行一个强制类型转换为字符型的地址 通过强制类型转换后再对这个字符型指针进行解引用就可以取到一个字节的数据继而对其进行一个判断如果为1的话那就是【小端】反之则是【大端】 char* p (char *)a; if (p 1){printf(小端\n); } else {printf(大端\n); }通过学习了函数章节我们可以对其进行一个分装~~ int check_sys(int num) {char p (char*)num;if (p 1) {return 1;}else {return 0;} } int ret check_sys(1);或者对于这个if判断我们可以就直接写成解引用的形式然后对其去进行一个判断 if ((char*)num 1)那既然是我们要return 1或者0的时候其实在解引用获取到低地址的第一个字节时直接return即可~~ int check_sys(int num){return (char)num; }3、数据范围的介绍 3.1 char与signed char数据范围 首先我们通过下面这幅图来看一看对于有符号的char和无符号的char 在内存中所能表示的范围各自是多少 【signed char】-128 ~ 127【unsigned char】0 ~ 255 char数据类型在内存中占1个字节也就是8个比特位。若是从00000000开始存放每次1上去然后逢二进一之后你就可以得出最大能表示的正整数为【127】可是在继续1后又会进行进位然后变为10000000符号位为1这是-128这是为什么呢 在内存中都是以【补码】的形式进行存放所以我们看到的1 1111111只不过是补码的形式 但是对于10000000我们直接将其记作【-128】它就对应的【-128】在内存中的补码通过去写出【-128】的原、反、补码可以发现是需要9个比特位来进行存放对于char类型的数值而言只能存放8个比特位因此在转换为补码之后会进行一个截断 最后剩下的就是10000000即为有符号char的最小负数为【-128】
3.2 unsigned char数据范围 因为是无符号char所以第一位不作为符号位就是从0 ~ 255 4、笔试题 有符号的数在整型提升的时候补符号位无符号的数在整型提升的时候补0 %u 是打印无符号整型认为内存中存放的补码对应的是一个无符号数%d 是打印有符号整型认为内存中存放的补码对应的是一个有符号数 第一道 #include stdio.h int main() {char a -1;signed char b -1;unsigned char c -1;printf(a %d, b %d, c %d, a, b, c);return 0; }我们来分析一下可以看到【a】和【b】都是有符号位的char类型那它们就是一样的现在将-1存放到这两个数据中去首先你应该要考虑到的是一个数据放到内存中去是以补码的形式所以我们首先将-1转换为补码的形式 1 0000000 00000000 00000000 00000001 1 1111111 11111111 11111111 11111110 1 1111111 11111111 11111111 11111111可是呢需要存放的地方又是char类型的变量只能存放8个字节无法放得下这32个字节因此便需要进行一个截断的操作放到变量a和变量b中都只剩下11111111这8个字节。对于变量c来说它是一个无符号的char类型变量不过-1存放到它里面还是11111111这8个字节不会改变只不过在内存中的变化与有符号char不太一样~~ printf(a %d, b %d, c %d, a, b, c);以%d的形式进行一个打印但是呢三个变量所存放的都是char类型的变量因此会进行一个整型提升只是有符号数的整型提升和无符号数不太一样 //a b - 有符号数 11111111111111111111111111111111 - 补符号位//c - 无符号数 00000000000000000000000011111111 - 补0在进行整型提升之后这些二进制数据还是存放在内存中的可是要输出打印在屏幕上的话还要转换为【原码】的形式 11111111111111111111111111111111 10000000000000000000000000000000 10000000000000000000000000000001 —— 【-1】00000000000000000000000011111111 —— 【255】第二道 #include stdio.h int main() {char a -128;printf(%u\n,a);return 0; }同理一个整数存放到内存中首先要将其转换为【补码】的方式 10000000 00000000 00000000 10000000 11111111 11111111 11111111 01111111 11111111 11111111 11111111 10000000接着因为这32个二进制位要存放到一个char类型的变量中因为进行截断为10000000然后在内存中需要进行一个整型提升char类型的变量将会填充符号位11111111111111111111111110000000执行打印语句可以看到这里是以%u的形式进行打印认为在内存中存放的是一个无符号整数。我们知道对于无符号整数来说不存在负数所以其原、反、补码都是一样的因此在打印的时候就直接将其转换为十进制进行输出 printf(%u\n,a);输出 不信的话还可以使用计算器来算一下 第三道 #include stdio.h int main() {char a 128;printf(%u\n,a);return 0; }接下去我们来看第三道题可以看出和上面那题基本基本一样只是把-128变成了128而已如果是【128】的话放到内存中就不需要像负数那样还要进行很多的转化了因为正数的原、反、补码都一致当我们内存中真正存的是10000000又因为是%u的形式打印然后需要整形提升 00000000 00000000 00000000 10000000然后我们可以看到时10000000符号位是1所以整形提升的时候补1 11111111 11111111 11111111 10000000所以还是和上题一样这里就不多赘述~~ 第四道 int main() {int i -20;unsigned int j 10;printf(%d\n, i j);return 0; }下面是解法 int main() {int i -20;//1 0000000 00000000 00000000 00010100//1 1111111 11111111 11111111 11101011//1 1111111 11111111 11111111 11101100unsigned int j 10;//0 0000000 00000000 00000000 00001010printf(%d\n, i j);//1 1111111 11111111 11111111 11101100//0 0000000 00000000 00000000 00001010//——————————————//1 1111111 11111111 11111111 11110110//1 1111111 11111111 11111111 11110110//1 0000000 00000000 00000000 00001001//1 0000000 00000000 00000000 00001010 —— 【-10】//按照补码的形式进行运算最后格式化成为有符号整数return 0; }本次我们用到的是两个int类型的数据一个是有符号的一个是无符号的。但无论是有符号还是无符号放到内存中都是要转换为补码的形式就是对算出来的两个补码一个二进制数的相加运算注意这里是将整数存放到int类型的变量中去所以不需要进行【截断】和【整型提升】 1 1111111 11111111 11111111 111011000 0000000 00000000 00000000 00001010 ——————————————1 1111111 11111111 11111111 11110110在运算之后要以%d的形式进行打印输出那就会将内部中存放的补码看做是一个有符号数既然是有符号数的话就存正负可以很明显地看到最前面的一个数字是1所以是负数要转换为原码的形式进行输出 1 1111111 11111111 11111111 11110110 1 0000000 00000000 00000000 00001001 1 0000000 00000000 00000000 00001010 —— 【-10】第五道 接下去第五道是一个for循环的打印 int main() {unsigned int i;for (i 9; i 0; i–){printf(%u\n, i);}return 0; }运行结果:死循环 有同学就很诧异为什么会陷入死循环呢这不是就是一个正常的打印过程吗 其实问题就出在这个unsigned把它去掉之后就可以正常打印了 回忆一下我们在将无符号整数的时它的数据范围是多少呢 对于char类型来说是0 ~ 255;对于short来说是0 ~ 65536;对于int类型来说是0 ~ 16,777,215 对比进行观察其实可以发现它们的数值范围都是 0的所以对于无符号整数来说就不会存在负数的情况。因此这个for循环的条件【i 0】其实是恒成立的若是当i 0再去–此时就会变成【-1】 对于【-1】我们有看过它在内存中的补码形式为11…11是全部都是1而此时这这个变量i又是个无符号的整型所以不存在符号位这一说那么在计算机看来它就是一个很大的无符号整数。此时当i以这个数值再次进入循环的时候继续进行打印然后执行–i最后知道其为0的时候又变成了-1然后继续进入循环。。。 光是这么说说太抽象了我们可以通过Sleep()函数在打印完每个数之后停一会来观察一下 #include windows.hint main() {unsigned int i;for (i 9; i 0; i–){printf(%u\n, i);Sleep(200);}return 0; }接着你便可以发现当i循环到0的时候突然就变成了一个很大的数字这也就是印证了我上面的说法 第六道 本题和四五道的原理是一样的对于unsigned char来说最大的整数范围不能超过255所以当这里的【i】加到255之后又会再1就会变成00000000此时又会进入循环从0开始也就造成了死循环的结果 unsigned char i 0; int main() {for (i 0; i 255; i){printf(hello world\n);}return 0; }第七道 最后一道我们来做做综合一些的涉及到字符串函数strlen int main() {char a[1000];int i;for (i 0; i 1000; i){a[i] -1 - i;}printf(%d, strlen(a));return 0; }首先来看函数主体也是一个for循环在开头定义了一个char类型的数组大小为1000,。在循环内部呢对它们进行一个初始化那最后里面的数字一定是 -1 -2 -3 -4 -5 -6 -7 …但是呢我们在上面学习过的有符号signed char它和char是一样的数据的范围在【-128 ~ 127】所以当i加到128的时候这个位置上的值变为【-129】此时在计算机内部会将它识别成【127】同理【-130】会被识别成为【126】。。。依次类推最后当这个值为【0】的时候若再去减就会变成【-1】然后又变成【-2】【-3】【-4】。。。一直当这个i累加到1001的时候截止 我们要通过strlen()去求这个数字的长度对于strlen()来说求的长度是到\0截止那也就是上面的【0】不需要去关心后面的第二、三轮回 那其实这也就转变成了让我们去求有符号char在内存中可以存储多少个。这很简单范围是-128 ~ 127二者的绝对值一加便知为255
//-1 -2 -3 -4 -5 -6 -7 …-128 127 126 … 0 -1 -2 -3…. printf(%d, strlen(a));来看一下运行结果~~ 四、浮点型在内存中的存储 在第二模块我有提到过一个叫做【浮点数家族】里面包含了[float]和[double]类型对于浮点数其实我们不陌生在上小学的时候就有接触到的3.14圆周率还有以科学计数法表示的1E10 在计算机中整型类型的取值范围限定在limits.h浮点型类型的取值范围限定在float.h 我们可以在【everything】中找找有关float.h这个头文件 1、引入 首先要了解浮点数在内存中的存储规则我们要通过一个案例来进行引入。请问下面四个输出语句分别会打印什么内容 int main() {int n 9;float* pFloat (float*)n;printf(n的值为%d\n, n); //1printf(*pFloat的值为%f\n, *pFloat); //2*pFloat 9.0;printf(num的值为%d\n, n); //3printf(*pFloat的值为%f\n, *pFloat); //4return 0; }来简单分析一下整型变量n里面存放了9然后通过强制类型转换为浮点型的指针存放到pFloat中。 首先第一个去打印n的值毫无疑问就是9第二个对pFloat进行解引用访问对于float类型的指针与int一样都可以访问四个字节的地址所以解引用便访问到了n中的内容又因为浮点数小数点后仅6位有效因此打印出来应该是9.000000接下去通过pFloat的解引用修改了n的值不过第三个以%d的形式进行打印应该也还是9第四个的话也是以浮点数的形式进行打印那应该也是9.000000 可结果真的和我们想象的一样吗让我们来看一下~~ 可以看到我们猜测推理的4个里面对了两个中间的两个出了问题而且还是两个很古怪的数字对于n 和 *pFloat 在内存中明明是同一个数为什么浮点数和整数的解读结果会差别这么大 这要涉及到浮点数在内存中【存】与【取】规则接下去我们首先来了解一下这个规则~~ 2、浮点数存储规则 2.1 概念 根据国际标准IEEE电气和电子工程协会 754任意一个二进制浮点数V可以表示成下面的形式 V (-1)^S * M * 2^E (-1)^S表示符号位占一位。当S 0V为正数当S 1V为负数M【尾数】表示有效数字放在最低部分占用23位1 M 22^E表示指数位。E为指数占用8位【阶符采用隐含方式即采用移码方法来表示正负指数】 v 5.5 整数部分的5可以写成101这毋庸置疑但是这个小数部分的5要如何去进行转换呢对于0.5来说我们刚才看了小数部分的权重之后知道是2-1所以直接使这一位为1即可接着我们就要去求出S、M和E对于M来说是 1并且 2的不过这里的101.1却远远大于1所以我们可以通过在操作符中学习的【移位】操作将这个数进行左移两位但是左移之后又要保持与原来的数相同所以可以再乘上22使得与原来的二进制相同。接着根据公式就可以写出(-1)0 * 1.011 * 22这个式子然后可以得出S、M、E为多少了 v -5.5 这个和上面的一样就是前面加了一个负号那只是符号位进行了修改的话我们只需要变动S就可以了即S 1 v 9.5 如果知道了第一题如何计算的话这题也是同样的道理只是整数部分发生了一个变化而已
可并不是所有数的小数位都是0.5如果是0.25的话你可以将【2-2】置为1依次类推。。。可以对于下面这个3.3里面的0.3你会如何去凑数呢首先0.5肯定不能那只能有0.25但若是再加上0.125的话那就多出来了那应该配几呢其实你将后面的数一个个地去列出来就可以发现是没有数字可以配成0.3的。所以我们可以得出这个数字其实是无法在内存中进行保存。这也是为什么浮点数在内存中容易发生精度丢失的原因 进一步探索指数E与尾数M的特性 IEEE 754标准规定 对于32位的浮点数【float】最高的1位是符号位S接着的8位是指数E剩下的23位为有效数字M 对于64位的浮点数【double】最高的1位是符号位S接着的11位是指数E剩下的52位为有效数字M IEEE 754对有效数字M和指数E还有一些特别规定 首先是对于有效数字尾数M 前面说过 1 ≤ M 2 也就是说M可以写成 1.xxxxxx 的形式其中xxxxxx表示小数部分IEEE 754规定在计算机内部保存M时默认这个数的第一位总是1因此可以被舍去只保存后面的xxxxxx部分。比如保存1.01的时候只保存01等到读取的时候再把第一位的1加上去。这样做的目的是节省1位有效数字。以32位浮点数为例留给M只有22位但若是将第一位的1舍去以后等于可以保存23位有效数字精度相当于又高了一位 至于指数E情况就比较复杂 首先E为一个无符号整数unsigned int 那对于无符号整数来说我们在上面有介绍过如果E为8位它的取值范围为[0 - 255]如果E为11位它的取值范围为[0 - 2047] 但是我们知道科学计数法中的E是可以出现负数的所以IEEE 754规定存入内存时E的真实值必须再加上一个中间数对于8位的E这个中间数是127对于11位的E这个中间数是1023 简单一点还是上面讲到过的5.5注意如果定义成【float】类型的变量的话要在后面加上一个f作为和【double】类型的区分
float f 5.5f;然后我们对5.5这个数字去进行分析它存入到内存中的样子。通过上面算出来的 S 0, M 1.011, E 2去写出这32位浮点数存放到内存中是一个怎样的形式 对于符号数S来说就是把0存进去占一位 对于指数E来说为232位浮点数要加上一个中间值127所以要存入的十进制数为129再将其转换为8位二进制即为10000001 对于尾数M来说需要舍去整数位1然后将【小数部分】的011这三位放到内存中但是规定了M为23位此时我们只需要在后面补上20个0即可 然后便可以对这个32个比特位进行划分8位一个字节得出40 b0 00 00 还有一点莫要忘了还记得我们上面讲到的【大小端】存放吗要存放到内存中的最后一步就是将其进行小端存放【这是我的机器】即为00 00 b0 40 如何将浮点数从内存中【读取】出来呢? 指数E从内存中取出还可以再分成三种情况

  1. E不全为0或不全为1 对于这种情况就是最普通的若是E存放在内存中的8位二进制数不全为0或者不全为1的话那么直接按照上面说到过多一些M与E【写入内存】的规则进行一个逆推即可以32位浮点数为例因为我们在计算指数E的时候加上了一个127那么此时减去127即可在计算尾数M的时候舍去了整数部分的1那次此时再补上这个1即可
  2. E全为0 对于E全0的这种情况很特殊也就是意味着8位二进制全为0即00000000这个情况是在指数E加上127之后的结果那么原先最初的指数是多少呢那也只能是-127了。那如果这个指数是-127的话也就相当于是【1.xxxx * 2-127】是一个非常小的数字几乎是和0没有什么差别这时浮点数的指数E等于1-127或者1-1023即为真实值对于尾数M不再加上第一位的1而是还原为0.xxxxxx的小数。这样做是为了表示±0以及接近于0的很小的数字
  3. E全为1 最后一种就是当E全为1的时候即11111111这个情况也是在指数E加上127之后的结果那么原先最初的指数是多少呢那便能是128了那也只能是-127了。那如果这个指数是-127的话也就相当于是【1.xxxx * 2128】是一个非常大的数字这时如果尾数M全为0表示±无穷大正负取决于符号位s 以上就是有关浮点数如何【写入】内存和从内存中【读取】的所有相关知识 3、开局疑难解答 int main() {int n 9;float* pFloat (float*)n;printf(n的值为%d\n, n); //1printf(*pFloat的值为%f\n, *pFloat); //2*pFloat 9.0;printf(num的值为%d\n, n); //3printf(*pFloat的值为%f\n, pFloat); //4return 0; }首先写出【n 9】在内存中的补码0 0000000 00000000 00000000 00001001。然后是将这个n的地址存放到了一个浮点型的指针中去 int n 9; float pFloat (float*)n;那么此时进行一个打印以%d的形式打印n不用考虑就是9但是后一个就不一样了对浮点型的指针进行解引用那也就是要将存放在内存中的浮点数进行读取出来 printf(n的值为%d\n, n); printf(*pFloat的值为%f\n, *pFloat);那编译器此时就会站在浮点数的角度去看待这个00000000000000000000000000001001将第一位看做是符号位0即S 0然后接下去就是8个比特位E不过可以发现这个E是全0呀00000000就是我们上面所讲到的这种特殊情况那此时后面的尾数M就不可以添上前面的整数部分1了而是应该写成0.xxxxxx的形式即0.000000000000000000001001。那对于指数E应该等于1-127为【-126】。所以最后写出来v的形式为 (-1)^0 * 0.000000000000000000001001 * 2^ (-126)此时我们再去计算这个值打印的结果是多少其实完全不需要计算仔细观察就可以发现S为1M为一个很小的数若E再去乘上这个很小的数那只会更小然后无限接近0,。那最后根据这个浮点数小数点后6位有效便可以看出最终的结果为0.000000 然后我们再来看下面的。此时pFloat进行解引用然后将9.0存放到n这块地址中去那也就相当于是我们最先学习了如何将一个浮点数存放到内存中去 *pFloat 9.0;那我们可以很快将其转换为二进制的形式1001.0然后通过v的公式得出(-1)^0 * 1.001 * 2^3此时再去将其转换为IEEE 754标准规定的32位浮点数。首先看到指数E为3加上127之后为130那么二进制形式即为10000010尾数M也是同理舍去1后看到001后面添上20个0补齐23个尾数位。最后的结果即为 —— 0 10000010 00100000000000000000000然后去执行打印语句那我们以浮点数的形式放进去但是以%d的形式打印n那么这一串二进制就会被编译器看做是补码既然是打印就得是原码的形式不过看到这个符号位为0那我们也不需要去做一个转换它就是原码 printf(num的值为%d\n, n);那么最后机器就会将二进制形式的原码转换为十进制的形式然后打印。一样我们可以将它放到【程序员】计计算器进行运行然后找到十进制的形式便是最后打印输出在屏幕上的结果 01000001000100000000000000000000 —— 1,091,567,616好了本文到这里就结束了感谢大家的收看