工程公司手机网站,wordpress调用最近发布的文章,it人力外包公司,公司网站的设计风格大多是整数在内存中的储存#xff08;大小端字节序#xff09;
1.整数在内存中的储存 2.大小端字节序 3.整数在内存中储存例子 4.字节序判断 5.死循环现象 文章目录 整数在内存中的储存#xff08;大小端字节序#xff09;整数在内存中的储存大小端字节序什么是大小端为什么会有…整数在内存中的储存大小端字节序
1.整数在内存中的储存 2.大小端字节序 3.整数在内存中储存例子 4.字节序判断 5.死循环现象 文章目录 整数在内存中的储存大小端字节序整数在内存中的储存大小端字节序什么是大小端为什么会有大小端 整数在内存中储存例子字节序判断死循环现象 整数在内存中的储存
在学操作符的时候我们就知道整数的2进制表示方法有3种原码反码补码 对于有符号整型这三种表示方法都由符号位和数值位组成符号位’0‘表示’正‘’1‘表示’负‘。 最高位为符号位其余的为数值位。
对于有符号整型 正整数原码反码补码相同 负整数 原码直接将数值按照正负形式翻译成二进制 反码原码符号位不变数值位取反 补码反码加1 对于无符号数据 原码反码补码相同 对于整形数据来说内存中存放的是数据的补码 因为补码可以对符号位和数值域统一处理同样加法减法也可以统一处理补码与原码互相转换他的运算过程是相同的不需要额外的硬件电路 大小端字节序
在学习大小端字节序之前我们先来梳理几个小知识 1.一个字节——8个二进制位 一个16进制位——4个二进制位 两个16进制位——8个二进制位 2.在计算机系统中内存被分为一个个字节单元字节单元的编号地址 下面给一个代码试着调试看一看
int main()
{int a 0x11223344;//这里给出一个整型a用16进制给初始化一下return 0;
}调试看结果 我们不难看出a中的0X11223344这个数字是按照字节为单位倒着存放的这是为什么呢
这就不得不提到大小端了。
什么是大小端
当我们在内存储存超过一个字节的数据时就存在储存顺序的问题我们分为大端字节序储存和小端字节序储存
大端储存 是指数据的低字节内容保存在高地址处而数据得高字节内容保存在低地址处。 小端储存 是指数据得低字节内容保存在低地址处而数据的高地址内容保存在高地址处。 上边使用的VS2022他是小端储存模式所以按照小端的储存模式规则他在内存中字节序是倒着储存的。
为什么会有大小端
存在大小端模式之分的原因是在计算机系统中我们是以字节为单位的每个地址单元都对应一个字节一个字节为8个bit位但在C语言中除了8个bit位的char类型还有16个bit的short型32个bit的long型看具体编译器了此外对于位数大于8的处理器比如16位或32位的处理器由于寄存器宽度大于一个字节那么必然存在着一个如何将多个字节安排的问题这就导致了大端储存模式和小端储存模式。
比如对于一个16个bit位的X,他在内存中的地址为0x0010,X的值是0x1122,那么0X11是高字节位0X22是低字节位在大端模式中0x11应该放在低地址处即0X00100X22应该放在高地址处即0X0011中。小端模式则恰好相反。
我们常用的X86结构是小端模式而KEIL C51为大端模式。很多的ARM,DSP都是小端模式。有些ARM处理器还可以由硬件来选择大小端
整数在内存中储存例子
1.写一个程序来判断当前机器的字节序 分析一波 我们可以先创建一个整型变量int a1 这个数据在小端模式下储存的应该是 01 00 00 00 大端模式下储存的应该是 00 00 00 01 因此想要判断当前机器是那种字节序只需要拿出第一个字节比一比就行
如何把它取出来呢
我们知道当我们对一个数据取地址时得到的地址是较小的字节单元的地址这一点可以看一下博主的指针知识点总结有讲到这里而内存的储存是从低地址向高地址储存的因此我们可以对a取地址就可以锁定到第一个字节再将它强制类型转换成字符指针类型然后解引用就可以得到第一个字节了 即 *char *)a; 由此写出代码
int Check_c(int i)
{return *(char*)i;
}
int main()
{int i 5;int retCheck_c(i);if (ret 5)printf(小端\n);//如果当前的系统储存方式是小端就能输出5如果不是小端返回值就不会是5elseprintf(大端\n);return 0;
}下面再通过几道题再理解一下
2.
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;
}再分析之前先来复习一下整型提升的知识 对于有符号整数提升是按照变量的数据类型的符号位来提升 对于无符号整数提升高位补0 分析一波 我们先写出-1的原码反码补码 -1 原码10000000 00000000 00000000 00000001 反码11111111 11111111 11111111 11111110 补码11111111 111111111 11111111 1111111 又因为a,b,c均是char类型这里截断一下a,b,c都是 11111111
在输出的时候要以有符号整形输出那么就要发生整型提升
对于a,他是有符号的char类型高位补1得到完整的补码 11111111 11111111 11111111 11111111 对其补码取反加1得到的原码为 10000000 00000000 00000000 00000001 输出仍为-1而对于b,他也是有符号char类型与a一样输出-1
对于c,他是无符号类型做完整型提升得到补码 00000000 00000000 00000000 11111111 同时这也是c的原码输出255
看一下运行结果 3.
int main()
{char a 128;char b -128;printf(a%u b%u, a, b);return 0;
}在分析之前还是先来复习一下知识 char类型通常是一个8位的有符号整数取值范围在-128~127 unsigned char 类型是8位的无符号整数取值范围在0~255 这里我们分析a,b,他们的原反补码如下 -128 原码10000000 00000000 00000000 10000000 反码11111111 11111111 11111111 01111111 补码11111111 11111111 11111111 10000000 a,b是char 类型截断得到a,b是 10000000 对a,b整型提升后补码 11111111 11111111 11111111 10000000 因为输出的是无符号整型a,b的原码反码补码一样输出的是一个很大的数 看运行结果 在这里尽管a128,已经超出char类型的取值范围但并不影响因为在char类型里边他总会把赋给它的值通过各种截断让他在他的取值范围内 而我们看到在截断后a,b的值是一样的所以他整型提升后结果也是一样的 4.
int main()
{unsigned char a 200;unsigned char b 100;unsigned char c 0;c a b;printf( % d % d, a b, c);return 0;
}先分别求出a,b的反码 a的反码00000000 00000000 00000000 11001000 b的反码00000000 00000000 00000000 01100100 那么在unsigned char的类型中 a:11001000 b:01100100 ab:1 00101100 对他做整型提升高位补0 00000000 00000000 00000001 00101100原码反码补码——对应的就是十进制的300 而cab;, 虽然unsigned char 下的ab仍为1 00101100但c是char类型要丢掉高位的1c为 00101100对其进行整型提升高位补0 00000000 00000000 00000000 00101100,对应十进制的44 字节序判断
1.
unsigned int a 0x1234;
unsigned char b *(unsigned char *)a;在32位大端模式处理器上变量b等于 分析
对于a,a0x00001234,前边的0被省略掉了那么在大端模式下他在内存中字节的储存顺序为 00 00 12 34 *(unsigned char *)a;这个操作就是把低地址的字节00拿出来在上边写个代码判断当前机器是大端小端的时候就已经介绍过了 *(unsigned char *)a;被赋值给b,b的值就是0x00 当以“%x形式输出16进制时0x,和前边的0都会被省略 如果想要输出0x,就以%#x形式输出 2. //X86环境小端字节序
int main()
{int arr[4] { 1,2,3,4 };int* ptr1 (int*)(arr 1);int* ptr2 (int*)((int)arr 1);printf(%x %x, ptr1[-1], *ptr2);return 0;
}在小端模式下数组元素在内存中字节的储存顺序是 对ptr1 arr1,对整个数组取地址加1跳过整个数组 ptr1[-1]*(ptr1-1) 指向如图所示04 00 00 00那么对应的数据就是0x 00000004,0x,和前边的0省略掉输出4
对于ptr2 arr指向的是arr首元素地址。假设arr0x0000EF10,将他强制转换成整数类型整数类型加1就是单纯加个1int)arr1)0x0000EF11,跳过一个地址字节跳到下一个字节这时对ptr2解引用指向的是 00 00 00 02 对应的数据是0x 02 00 00 00,输出2000000 死循环现象
这里我们在学习一个知识 我们通过这个图片可以明白在char类型中他的取值范围是形成一个圆环一直在循环的其他类型也是如此也就是说比如我们给char a500,很显然他超出了char的取值范围可是根据这个规律当他截断后放在char类型里边的数就是500%256244对应的就是-12。 再例如unsigneg char b400,也超出了unsigned char 的范围根据这个规律当他截断后放在unsigned char类型里的数是400%256144对应的就是144 运行的结果也是如此
学习过这个之后就可以看几道题了 1. #includestdio.h
#includestring.h
int main()
{char arr[1000];int u 0;for (u 0; u 1000; u){arr[u] -1 - u;}printf(%d, strlen(arr));return 0;
}
分析一波 strlen计算数组元素的个数并且必需要碰到‘\0’才能结束 在上边代码中初始化arr数组如果不考虑arr的类型他的初始化值是从-1到-999可是他受到char类型的限制char类型的取值范围在-128~127这时候就要发生截断总之char类型会想办法让初始化的值在他的取值范围内。那么arr的初始化值就会是
-1-2-3…-127,-128,-129这时候-129已经超出char的范围char想办法让他满足自己的范围那么根据上边学习的规律-129放在char类型里就是127
在接下来就是127126125…2,1,0,-128,-127.就这样循环下去 因为’\0’对应的ASCII码是0所以strlen会计算’\0’之前的字符个数为128127255 2.
int main()
{unsigned int i 0;for (i 0; i 255; i){printf(haha\n);}return 0;
}
int main()
{unsigned int j 0;for (j 12; j 0; j--){printf(hehe\n);}return 0;
}这两段代码无一例外他们的结果最后都是死循环。 分析 对第一个 unsigned int类型的取值范围0到255在第一个循环中i加到266时超出unsigned int的范围unsigned int将他转化成符合自己范围的数值即根据规律计算出是0这样循环条件恒成立就陷入了死循环 同理第二段代码 当j 减到0时再减变为-1不满足unsigned int的取值范围unsigned int将他转化成符合自己范围的值根据上边学习的内容计算出时255循环条件恒成立陷入死循环 作者有话说作者只是一只小白以上解释均是作者对已学知识的理解巩固复习。希望可以帮到大家如有错误感谢指出