代码改变世界

C语言第21讲 - 指南

2025-09-29 20:47  tlnshuju  阅读(8)  评论(0)    收藏  举报

整数在内存中的翻译

                整数的二进制表示方式有3种,即原码,反码,补码

在内存中存放的是补码

类型存入翻译和类型取出翻译,数据存储方式和数据取出方式,都是同操作流程的相反顺序

在讲翻译前需要引入个概念  符号位

符号位表示的就是 数据在正负符号  

例如:9 其实是 +9  (常常省略+号) -9 

所以在32个比特位最前方一位表示为符号 0正 1符负

                现讲,整形类型的翻译方式

                整形的翻译方式会根据,整形的正负数分两类

正数:

                正数的原码是本身的二级制

                正数特殊的点在于 原码 反码 补码 都相同

                例如:1的2进制(int为4字节,有32个比特位)

                0 0000000 00000000 00000000 00000001

负数:

负数的原码也是本身二进制(正负原码翻译的没区别)

                反码为 符号为不变,其它位0变1,1变0

                补码为 在反码的基础上+1

                例如:-1的全流程图如下

负数取出数据就是 -1 再 符号位不变 0变1,1变0;只不过顺序流程倒置而已

不过取出 将补码 符号位不变 0变1 1变0 再+1也能得到原码

注意上面说的是两个方式,选一个就可以得到原码

数据在内存中的存放

                我们常常会发现,数据在内存中存放是按比例反向的

我们去观察以下代码在内存中单存放

#define _CRT_SECURE_NO_WARNINGS
#include 
int main()
{
	int a = 0x11223344;
    return 0;
}

能发现,这里的存放是按字节为大小,反向存放着。这样的存放方式就叫做小端字节序

                大小端字节序

其实超过一个字节的数据在内存中存储的时候,就会有存储顺序的问题。按照不同的存储顺序,我们分为大端字节序和小端字节序存储

                字节序存储 按照一个字节一个字节的大小分开数据,按某种顺序存储

                这个顺序的分辨在与小端和大端

小端字节序:

将低位字节的内容存储到低地址处,高位字节的内容存储到高地址处

例如:

                123 可以分出   个位3   十位2   百位1

                个位低位 百位为高位

                而数据会翻译成补码

                再以一个字节大小分出高低位,分别存放

  大端字节序:

讲低位字节内存存储到高位地址处,高位字节序内容存储到低地址处

问题:设计一个小程序来判断当前机器的字节序

#define _CRT_SECURE_NO_WARNINGS
#include 
int test()
{
	int a = 1;
	char* p = &a;
	return *p;
}
int main()
{
	if (test() == 1)
		printf("小端");
	else
		printf("大端");
	return 0;
}

题目2:

#include 
 int main()
 {
 char a= -1;                            // -1 VS. char是signed char 这个取决于编译器
 signed char b=-1;                      // -1
 unsigned char c=-1;                    // 255
 printf("a=%d,b=%d,c=%d",a,b,c);
 return 0;
 }
 unsigned char c=-1;
 printf("%d",c);

结果为255的过程如下图判断

问题3:

 #include 
 int main()
 {
 char a = -128;
 printf("%u\n",a);        // 4294967168
 return 0;                // 11111111 11111111 11111111 10000000
 }

问题4:

#include 
 int main()
 {
 char a = 128;
 printf("%u\n",a);        // 4294967168
 return 0;                // 11111111 11111111 11111111 10000000
 }

128和-128在截取时都是1000 0000 后续操作一样值一样

问题5:

#include 
 int main()
{
char a[1000];
 int i;
 for(i=0; i<1000; i++)
 {
 a[i] = -1-i;
 }
 printf("%d",strlen(a));        // 255
 return 0;
}

这里有个结论图:

问题5:

 #include 
 //X86环境 ⼩端字节序
int main()
 {
 int a[4] = { 1, 2, 3, 4 };
 int *ptr1 = (int *)(&a + 1);
 int *ptr2 = (int *)((int)a + 1);     // 0x00 00 00 04   0x 02 00 00 00
 printf("%x,%x", ptr1[-1], *ptr2);    // 4               2000000
 return 0;
 }

浮点数在内存中的存储

                任意⼀个⼆进制浮点数V可以表⽰成下⾯的形式

                V = (-1)^S  *  M  *  2^E

                • (-1)^S 表⽰符号位,当S=0,V为正数;当S=1,V为负数

                • M表⽰有效数字,M是⼤于等于1,⼩于2的

                • 2^E 表⽰指数位

举例来说:

                ⼗进制的5.0,写成⼆进制是 101.0 ,相当于 1.01×2^2

                那么,按照上⾯V的格式,可以得出S=0,M=1.01,E=2

观察上方式子可以发现,(-1) 和 2是固定常数,变化的位 S M E 三数所以存储只需要储存S M E 就好了

在存储 S M E 3数时还需要注意2点:

                1.由于 M 数 总是会变化成 1.xxxx的数,范围在(1,2)之间

                所以再存储时可以将1.xxxx的1不进行存储,在有效位存储时会多出1位

                2.将举例改为0.5时会发现E 会出现负数情况,所以 E在存储时会根据类型的不同做出一下措施:

                                1.32位 E+127(中间值)

                                2.64为 E+1023(中间数)

                                这中间数和E在内存中存储的内存空间大小相关

现在储存内容弄清后,接下来就是每个内容有多大存储空间

由国家标准规定:

  32位( float 4字节):

        64位( double 8字节)

浮点数取的过程 指数E从内存中取出还可以再分成三种情况:                

E不全为0或不全为1 :

                这时,浮点数就采⽤下⾯的规则表⽰,即指数E的计算值减去127(或1023),得到真实值,再将有效 数字M前加上第⼀位的1。 ⽐如:0.5的⼆进制形式为0.1,由于规定正数部分必须为1,即将⼩数点右移1位,则为1.0*2^(-1),其 阶码为-1+127(中间值)=126,表⽰为01111110,⽽尾数1.0去掉整数部分为0,补⻬0到23位 00000000000000000000000,则其⼆进制表⽰形式为:

0 01111110 00000000000000000000000

E全为0:

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

  • 这相当于指数被固定为最小的值(-126),然后只通过尾数部分来提供更精细的精度

0 00000000 00100000000000000000000

E全为1:

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

0 11111111 00010000000000000000000

问题:

 #include 
 int main()
 {
 int n = 9;
 float *pFloat = (float *)&n;
 printf("n的值为:%d\n",n);                    // 9
 printf("*pFloat的值为:%f\n",*pFloat);        // 0.000000
 *pFloat = 9.0;
                                              // 0 1000 0010 001 0000 00000000 00000000
 printf("num的值为:%d\n",n);                  // 1091567616
 printf("*pFloat的值为:%f\n",*pFloat);        // 9.000000
 return 0;
 }