家居企业网站建设精英,做网站最清晰的字体,自我介绍网页模板代码,拼多多网站建设本章概述 整数在内存中的存储大小端字节序和字节序判断练习1练习2练习3练习4练习5练习6 彩蛋时刻#xff01;#xff01;#xff01; 整数在内存中的存储
回忆知识#xff1a;在讲操作符的那章节中#xff0c;对于整数而言咱们讲过原码#xff0c;反码和补码。整数分为有… 本章概述 整数在内存中的存储大小端字节序和字节序判断练习1练习2练习3练习4练习5练习6 彩蛋时刻 整数在内存中的存储
回忆知识在讲操作符的那章节中对于整数而言咱们讲过原码反码和补码。整数分为有符号整数和无符号整数。有符号整数又分为正整数和负整数。对于有符号整数在内存中的存储为1个符号位剩下的全是数值位。对于无符号整数在内存中的存储为全是数值位。对于有符号整数而言符号位为1表示这个整数为负数符号位为0表示这个整数为正数。对于负整数它的原码反码和补码是不同的。对于正整数和无符号整数它们的原码反码和补码是一样的。对于整数而言我们在内存中存的是它们的补码。只有整数才有原码反码和补码的概念为什么要有原码反码和补码的概念呢 在计算机中整数在内存中存的是它的补码。计算机中只有CPU加法处理器没有CPU减法处理器。举个例子
// 我们写个数学式子5-23在数学中日常生活中我们都是直接这样写没啥问题。
// 但在计算机中只有CPU加法处理器所以我们要这样写5-23。我们要写成加法才能进行运算。所以当我们把整数以补码的形式存在内存中时就可以把符号位也参与计算这样就省了很多硬件设施。我们就以5-23为例子进行展示计算过程
// 5----原码 00000000 00000000 00000000 00000101 正整数的原码反码和补码是一样的
// 反码 00000000 00000000 00000000 00000101
// 补码 00000000 00000000 00000000 00000101// -2----原码 10000000 00000000 00000000 00000010
// 反码 11111111 11111111 11111111 11111101 原码取反
// 补码 11111111 11111111 11111111 11111110 反码1// 5 00000000 00000000 00000000 00000101-2 11111111 11111111 11111111 11111110 00000000 00000000 00000000 00000011 符号位为0正整数的原码反码和补码相同3所以以补码的形式存在内存中符号位也就可以参与计算。给大家提个小小的总结整数在内存中是以补码的形式存储而我们要打印的整数是以原码的形式取出的。
大小端字节序和字节序判断
大小端字节序前面咱们在C语言内存函数那章节中提到过大小端字节序。咱们提到过整形数据在内存中它的数值位和地址位是相反的——地位数据放在低地址处高位数据放在高地址处。进行结果调试图展示 我们创建的int i占有4个字节我们知道每4个bit位为1个16进制位所以我们创建的int i能够存储的下每个字节放两个16进制位。但是为什么整形数据在内存中的每个字节,存储的数据与我们的数据顺序不一样呢这就要引出大小端字节序了。为什么会有大小端字节序呢 先说个结论当任何数据的空间大小超过1个字节的时候就要考虑数据存放的顺序了。举个生活中的例子来理解月饼大家都吃过吧我们知道一个月饼礼盒里面装有很多的月饼一个月饼礼盒就相当于一块内存里面的每个月饼就相当于一个个数据。我们知道里面的月饼使用小盒包装的这些小盒包装就相当于1个字节。当我们把很多的小盒月饼放进这个礼盒的时候就要考虑存放的顺序了比如先放五仁馅还是先放草莓馅。这就要考虑放的先后顺序了。这也就是为什么当数据超过1个字节的时候要考虑存放的顺序了。如图所示 只要我们能顺利且正确的拿出我们想要的数据理论上我们可以随便存放数据只要我们把月饼有秩序的摆好在礼盒中我们想吃什么口味的月饼就可以直接拿。比如我们存放int i0x11223344,我们可以分为三大类进行存储如图所示 只要你能正确的取出想要的数据你可以随意排序。但是为了使C语言更有普及性就规定了标准对于整形数据的存储要不采用大端字节序要不采用小端字节序。具体使用那种方法由编译器决定。我们常见的是小端字节序。大小端字节序的概念 大端字节序在数据中从左向右左端是高位数据右端是低位数据。高位数据存放在低地址处低位数据存放在高地址处。提要在地址中左端为低地址右端为高地址所取的这个数据地址为这个数据的低地址如图所示;小端字节序高位数据放在高地址处低位数据放在 低地址处。如图所示
练习1
请简述⼤端字节序和⼩端字节序的概念设计⼀个⼩程序来判断当前机器的字节序。10分-百度笔试题。
// 思路讲解我们知道大小端字节序是按单个字节来计算的排序的所以我们可以创建一个简单的数据
对其访问一个字节就OK。比如int i 1; 1的大端字节序为 00 00 00 01。1的小端字节序为 01 00 00 00。
当我们访问1的一个字节时如果访问的数据是0它就是大端字节序访问的数据是1它就是小端字节序。进行代码展示
#define _CRT_SECURE_NO_WARNINGS 1
#include stdio.h
int check()
{int i 1;return *(char*)i;
}
int main()
{/*int i 0x11223344;*/if (check() 0)printf(大端字节序);elseprintf(小端字节序);return 0;
}
结果运行图 还有第二种代码这个代码要用到联合体进行代码展示
#define _CRT_SECURE_NO_WARNINGS 1
#include stdio.hunion un
{char i;int x;
}s;
int main()
{s.x 1;if (s.i 0)printf(大端字节序);elseprintf(小端字节序);return 0;
}
结果运行图 联合体这个知识点咱们后面会讲解的大家现在先了解一下。
练习2
大家可以先猜一下这个代码的运行结果。
#define _CRT_SECURE_NO_WARNINGS 1
#include stdio.hint main(){char a -1;signed char b -1;unsigned char c -1;printf(a%d,b%d,c%d, a, b, c);return 0;}
结果运行图 不知道大家猜出结果没咱们接下来进行讲解。
// -1 10000000 00000000 00000000 00000001 原码11111111 11111111 11111111 11111110 反码11111111 11111111 11111111 11111111 补码
// 由于char 只占一个字节所以-1要发生数据截断要截取低位数据放到char的空间中
char i -1 11111111
signed char char 11111111
unsigned char 11111111%d是打印有符号整数的占位符char ,signed char ,unsigned char这些数据类型都只有一个字节不够整形这个时候就要发生整形提升。
char i -1 11111111
整形提升 11111111 11111111 11111111 11111111 补码10000000 00000000 00000000 00000001 原码补码取反1打印 -1
signed char char 和char是一样的过程和结果 打印 -1
unsigned char 00000000 00000000 00000000 11111111 补码00000000 00000000 00000000 11111111 原码正整数和无符号整数原码反码和补码相同打印 255练习3
大家猜一下运行的结果-----------代码1。
#define _CRT_SECURE_NO_WARNINGS 1
#include stdio.hint main(){char a -128;printf(%u\n, a);return 0;}
结果运行图 进行代码解释
-128 10000000 00000000 00000000 10000000 原码11111111 11111111 11111111 01111111 反码11111111 11111111 11111111 10000000 补码%u打印的是无符号整形
char a 10000000 补码整型提升 11111111 11111111 11111111 10000000 补码被%u当成无符号整形全是数值位11111111 11111111 11111111 10000000 原码打印 4294967168代码2。
#define _CRT_SECURE_NO_WARNINGS 1
#include stdio.hint main(){char a 128;printf(%u\n, a);return 0;}
结果运行图 进行代码解释
128 00000000 00000000 00000000 10000000 补码
char a 1000000011111111 111111111 11111111 10000000 整形提升11111111 111111111 11111111 10000000 原码打印 4294967168通过上面的3个代码练习不知道大家有没有感知到什么重要的信息没有重要信息——数据类型的意义。它有两个意义1.数据类型决定了我们申请空间的大小和访问空间的多大权限。比如char能申请1个字节int 能申请4个字节char* 只能访问一个字节的空间int*能访问4个字节的空间这就是权限2.数据类型决定了我们如何看待这个数据。比如-1为整形但是想放到char里面就要发生数据截断。%d打印的是有符号整形数据在%d眼里面数据就是无符号整形在%u眼里面数据就是无符号整形。
练习4
大家猜一下运行的结果。
#define _CRT_SECURE_NO_WARNINGS 1
#include stdio.hint main(){char a[1000];int i;for (i 0; i 1000; i){a[i] -1 - i;}printf(%d, strlen(a));return 0;}
结果运行图 进行代码解释
观察这个代码我们能够分析出来它是给数组赋值的代码我们先不考虑为什么结果与我们的预期不一样。
由分析可知最终赋值的结果为 -1 -2 -3-4……-128-129-130……-1000。照咱们这样分析的话
结果应该是1000为什么是255呢这就要好好来分析一下char这个数据类型了我们知道chart是字符类型用来定义字符的。但是前面咱们也遇到过char定义整形的比如char i128;咱们讲个重要的知识点char类型包括unsigned char是整形的一种特殊形式它不仅可以定义字符还可以定义整数。那么接下来我们来分析一下char。
char占有1个字节分为char和unsigned char 进行讨论
char里面的可能存储-128~127
1位符号位7位数值位
0000 0000 --0
0000 0001 --1
0000 0010 --2
0000 0011 --3
0111 1111 --127
…… 整形提升 原码
1000 0000 -- 11111111 11111111 11111111 10000000--10000000 00000000 00000000 10000000 -128
1000 0001 -- 11111111 11111111 11111111 10000001--10000000 00000000 0000000 01111111-127
1000 0010-- -126
……
1111 1111 -- 11111111 11111111 11111111 11111111--10000000 00000000 00000000 00000001 1 我们把char的所有情况已经列出来了我们发现它最终又回到原来起始的数值我们可以画个它的数值循环图如图所示 当我们的数值超过char的范围的时候就要重新开始。
unsigned char里面的存储0~255
全是数值位
0000 0000 --0
0000 0001 --1
0000 0010 --2
……
1111 1111 --255unsigned char也符合一个循环图如图所示 有了上面的知识铺垫咱们就可以分析我们上面的代码了分析如下
我们知道strlen函数统计的是 \0前面的数据遇到 \0就停止了。我们讲过字符 \0的AS||值是0
所以当统计的是字符时strlen遇到 \0就停止统计的是 \0前面的数据。当统计的是整数时
遇到0就停止统计0之前的数据。
char a[1000] -1,-2,-3,……-128127126……0遇到0停止。
所以输出结果255大家可以类比一下int和unsigned int 可以自行画个循环图。
练习5
大家猜一下运行的结果。
#define _CRT_SECURE_NO_WARNINGS 1
#include stdio.h
int main()
{unsigned char i 0;for (i 0; i 255; i){printf(hello world\n);}return 0;}
结果运行图 我们发现结果直接死循环为什么会这样呢接下来进行分析
unsigned char 的取值范围0~255 。因为条件是i255.所以当i增加到255时还是符合条件的
继续执行程序只有增加到256时才会停止程序。前面由咱们画的循环图可知
当大于255时就回到起始数值0以此0再次参与计算因此就会死循环。我们来个举一反三看如下的代码
#define _CRT_SECURE_NO_WARNINGS 1
#include stdio.h
int main()
{unsigned int i;for (i 9; i 0; i--){printf(%u\n, i);}return 0;
}
结果运行图 可以看出来结果也是死循环。这个结果的原因和刚才代码的原因是相同的大家可自行分析一下。
练习6
大家猜一下运行的结果注意在X86条件下运行代码。
#define _CRT_SECURE_NO_WARNINGS 1
#include stdio.h
//X86环境 ⼩端字节序
int main()
{int a[4] { 1, 2, 3, 4 };int* ptr1 (int*)(a 1);int* ptr2 (int*)((int)a 1);printf(%x,%x, ptr1[-1], *ptr2);return 0;
}
结果运行图 进行代码讲解a取出的是整个数组的地址a1跳过整个数组这些都是前面的知识点忘记的同学自行回顾去。如图所示 我们先来分析一下这两个代码int* ptr1 (int*)(a 1);和ptr1[-1]。 int* ptr1 (int*)(a 1);这个代码中a1取到的是4后面的空间地址。然后把数组指针转为int*指针——int *(a1),赋值于ptr1。到目前为止ptr1[-1]这种数组的访问方式还是第一次见[-1]的意思就是在原来的位置上往前访问1个空间[-2]就是在原来的位置上往前访问2个空间。如图所示 所以ptr1[-1]访问的结果是4. 我们再来解释一下这两个代码int * ptr2 (int *)((int)a 1);和 *ptr2。数组名就是地址首元素的地址inta就是把地址转换成了整形。假设a的地址就是 0x11223344,(int)之后这个地址编号就变成了整形数据了它不再是地址了。所以inta1就是个普通的整形计算0x1122334410x11223345。然后。把这个值赋值再转换位整型指针—— (int *)((int)a 1)赋值给ptr2。所以当我们 *ptr2访问的就是个很大的整数——2000000。
彩蛋时刻
https://www.bilibili.com/video/BV1Jz4y1L7AR/?spm_id_from333.337.search-card.all.clickvd_source7d0d6d43e38f977d947fffdf92c1dfad 每章一句心中有火眼里有光。感谢你能看到这里点赞关注收藏转发是对我最大的鼓励咱们下期见