您的位置:首页 > 娱乐 > 八卦 > 梧州门户网站_建立的意思解释_北京seo收费_网站权重等级

梧州门户网站_建立的意思解释_北京seo收费_网站权重等级

2024/12/22 23:34:17 来源:https://blog.csdn.net/qq_44868502/article/details/144290803  浏览:    关键词:梧州门户网站_建立的意思解释_北京seo收费_网站权重等级
梧州门户网站_建立的意思解释_北京seo收费_网站权重等级

C 进阶 — 数据在内存中的存储

主要内容

1、数据类型详细介绍

2、整形在内存中的存储:原码、反码、补码

3、大小端字节序介绍及判断

4、浮点型在内存中的存储解析

一 数据类型介绍

基本内置类型

char        //字符数据类型  1
short       //短整型       2
int         //整形         4
long        //长整型		4
long long   //更长的整形    8
float       //单精度浮点数  4
double      //双精度浮点数  8// 规定    
sizeof(char)  ==  1
sizeof(char)  <=  sizeof(short)
sizeof(short) <=  sizeof(int)
sizeof(int)   <=  sizeof(long)
sizeof(long)  <=  sizeof(long long)

C 语言没有字符串类型,字符串类型是用字符数组实现的

类型:决定了开辟内存空间的大小和如何描述该块内存空间

1.1 类型的基本归类

整形

charunsigned charsigned charshortunsigned shortsigned short intunsigned intsigned intlongunsigned long signed long 

浮点型

float double

构造类型

> 数组类型
> 结构体类型 struct
> 枚举类型 enum
> 联合类型 union

指针类型

int *pi;
char *pc;
float* pf;
void* pv;

空类型

void 表示空类型(无类型)
通常应用于函数的返回类型、函数参数、指针类型

二 整型在内存中的存储

2.1 原码、反码、补码

计算机中的整数有三种二进制表示方法,即原码、反码和补码

三种表示方法均有 符号位 和 数值位 两部分,符号位都是用 0 表示 正 ,用 1 表示 负。数值位正数的原、反、补码都相同,而负整数的三种表示方法各不相同

  1. 原码 将数值按照正负数的形式翻译成二进制就可以得到原码
  2. 反码 原码的符号位不变,其他位依次按位取反就可以得到反码
  3. 补码 反码 +1 得到补码
2.2 原反补的意义

对于整形而言:数据存放内存中的是补码

因为可以将符号位和数值域统一处理,同时加减法也统一处理( CPU 只有加法器 )此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路

以下为原因探讨,参考

1、原码(Sign-Magnitude Representation)

原码是最简单的表示法,它用一位二进制数表示符号,0 表示正数,1 表示负数,其余位数表示数字的绝对值。例如,+5和 -5 的二进制原码分别是:

+5 的原码:00000101
-5 的原码:10000101

问题:原码虽然直观,但在进行二进制运算时,正负数的处理很麻烦,尤其是减法时,还需要额外判断符号位,增加了硬件电路的复杂性

2、反码(Ones’ Complement)

反码通过将负数的每一位进行取反( 1 变 0 ,0 变 1 )来表示负数。例如,+5 和 -5 的反码表示如下:

+5的反码:00000101(与原码相同)
-5的反码:11111010

优点:反码改进了原码的加减法处理,因为减法可以转化为加法,只需要加上一个负数的反码即可

问题:反码有一个严重的问题,即零的表示不唯一,存在 +0(00000000)和 -0(11111111)两种表示形式,这导致逻辑处理变得复杂

3、补码(Two’s Complement)

补码是最常用的表示法,它通过在反码的基础上再加 1 来表示负数。比如,+5 和 -5 的补码如下:

+5 的补码:00000101
-5 的补码:11111011(反码是 11111010,再加 1)

优点:① 唯一的零表示:补码只用一个零(00000000),解决了反码的双零问题 ② 加减法统一:补码的设计让加法和减法能够统一处理,所有数值都可以通过加法来计算,简化了硬件设计 ③ 符号处理自动化:补码的符号位(最高位)在计算过程中自动处理,无需额外逻辑

2.3 大小端

示例数据在内存中的存储

#include<stdio.h>/* 
短除法20/2 = 10 010/2 = 5  05/2  = 2  12/2  = 1  01/2  = 0  1    10100
*/
int main()
{    int a = 20; // 00000000 00000000 0000000 00010100// 14 00 00 00 十六进制 —> 二进制 (将每个十六进制数转换为 4 个二进制数字)// 00010100 00000000 00000000int b = -10; //10000000 00000000 00001010//取反 = 11111111  11111111 11110101//+1  = 11111111  11111111 11110110//转十六进制 = ff ff ff f6return 0;
}

可以看到对于 a 和 b 分别存储的是补码。但是发现顺序不一致

image-20241205161039490

image-20241205160916898

大小端

大端(存储)模式,是指数据低位保存在内存高地址中,而数据高位,保存在内存低地址

小端(存储)模式,是指数据低位保存在内存低地址中,而数据高位,保存在内存高地址

例如一个 16bit 的 short 型 x ,在内存中的地址为 0x0010 , x 的值为 0x1122 ,那么 0x11 为高字节, 0x22 为低字节。对于大端模式,就将 0x11 放在低地址中,即 0x0010 中, 0x22 放在高地址中,即 0x0011 中。小端模式,刚好相反

为什么有大小端之分 因为在计算机中,数据是以字节为单位的,一个字节为 8 bit。对于 32 位处理器,由于寄存器宽度大于一个字节,必然存在如何将多个字节安排的问题

2.4 练习

1、设计一个小程序判断当前机器的字节序

#include<stdio.h>
//方式一, 利用取地址 + 指针解引用
int check_sys()
{int i = 1;return *(char*) & i;
}// 方式二, 利用联合体特性
int check_sys()
{union {int i;char c;} un;un.i = 1;return un.c;
}int main()
{if (check_sys())printf("大端");elseprintf("小端");return 0;
}

2、下面程序分别都输出什么

/*① char 类型变量中存储的值的本质就是数字(存入字符时,字符会转化为 ASCLL 表中对应的数字再存入),因此char 类型的变量是可以直接存储数字的,只不过由于字节数的限制,存储范围较小② -1 的补码是 11111111111111111111111111111111,存入 char、signed char、unsigned char 类型变量中时,会发生截断,只将补码的八个低位端的 1 放入,即 11111111③ 打印时,由于打印格式是 %d,那么会发生整形提升。提升规则如下:有符号位则左端补充符号位,无符号位则左端补充 0。于是 a 补充为 11111111111111111111111111111111(char 一般默认为有符号型),b 补充为11111111111111111111111111111111,c 补充为 00000000000000000000000011111111。转化成原码输出,它们的结果分别是-1、-1、255
*/
#include <stdio.h>
int main()
{char a = -1;//-1 的补码 11111111111111111111111111111111 存入 char 发生截断, 即 11111111signed char b = -1;unsigned char c = -1;printf("a = %d, b = %d, c = %d",a,b,c); //打印时 %d 整形提升, 有符号位则左端补充符号位, 无符号位则左端补充0, 11111111111111111111111111111111 -> 转原码 -1return 0;
}
/*-128 = 10000000 00000000 00000000 10000000 原码= 11111111 11111111 11111111 01111111 反码= 11111111 11111111 11111111 10000000 补码存入 char 发生截断a =  10000000 (二进制)%u 按无符号整型输出, 发生提升 10000000 -> 00000000 00000000 00000000 10000000 补码-> 11111111 11111111 11111111 10000000 原码(补码取反 + 1, 这里要注意 %u 没有符号位, 所以原码不需要考虑符号位)
*/#include <stdio.h>
int main()
{char a = -128; //128 = 2^7 = 1000 0000printf("%u\n",a); //4294967168return 0;
}
/*128 = 00000000 00000000 00000000 10000000 原(补)码存入 char 发生截断a =  10000000 (二进制)%u 按无符号整型输出, 发生提升 10000000 -> 00000000 00000000 00000000 10000000 补码-> 11111111 11111111 11111111 10000000 原码(补码取反 + 1)
*/#include <stdio.h>
int main()
{char a = 128;printf("%u\n",a); //4294967168return 0;
}
/*-20 = 10000000 00000000 00000000 00010100 (原码)= 11111111 11111111 11111111 11101011 (反码)= 11111111 11111111 11111111 11101100 (补码)10  = 00000000 00000000 00000000 00001010 (原补码)+   = 11111111 11111111 11111111 11110110 (补码)->  = 10000000 00000000 00000000 00001010 (原码)这里要注意 %d 有符号位, 所以 11111111 11111111 11111111 11110110 也要被当成有符号数, 符号位为 1
*/
int i = -20;
// i + j, unsigned + signed, 就要转为 unsigned
unsigned  int  j = 10;
printf("%d\n", i + j); //-10 //最后一步需要注意 %d
//按照补码的形式进行运算,最后格式化成为有符号整数
/*9 = 00000000 00000000 00000000 00001001当 i == 0 时, i-- 则 i 变为 11111111 11111111 11111111 11111111 = 2^32 - 1死循环输出
*/
unsigned int i;
for(i = 9; i >= 0; i--)
{printf("%u\n",i);
}
/*数值 -1 - i = -1, -2, -3, -4-> 对应的二进制10000000 00000000 00000000 00010100char 1 个字节 8 bit-1 - i = 10000000 00000000 00000001 00000000 时存入 a[i] 中因发生截断, 存入数值 0 2^8 = 256, 此时 i = 256strlen 不包含 '\0', 因此 strlen 结果为 255阿拉伯数字 0 的ASCII码为 48,‘\0’ 转义字符的 ASCII码值为 0,它表示的是 ASCII 控制字符中空字符
*/int main()
{char a[1000];int i; //整型for(i=0; i<1000; i++){a[i] = -1-i;}printf("%d",strlen(a)); //255return 0;
}
/*i 是 unsigned char, 对应范围 [0, 255]当 i == 255 时, i++ 则 i = 0死循环打印
*/#include <stdio.h>
unsigned char i = 0;
int main()
{for(i = 0;i<=255;i++){printf("hello world\n");}return 0;
}

三 浮点数在内存中的存储

参考链接 浮点型在内存中的存储 ,浮点型在内存中的存储

浮点数在计算机内部的表示方式,根据国际标准IEEE754,任意一个二进制浮点数可表示成下面的形式:

(-1)^ S * M * 2 ^ E 其中(-1)^ S 表示符号位,当 S = 0 为正,当 S = 1 为负,M 表示有效数字,1 <= M < 22 ^ E 表示指数位

//如下所示符号位   有效数字	   指数
(-1)  ^   S   *   M  *  2 ^  E

举例,十进制的 7.0,二进制表示为 111.0,相当于1.11 * 2 ^ 2。那么,按照上面的格式,7 大于 0,S = 0;有效数字为 1.11,M=1.11;指数为 2,E = 2

再举个例子,对于十进制数 5.5,把它化成二进制数,为 101.1。5.5大于0,S=0;有效数字为 1.011,M = 1.011;指数为 2 E = 2

IEEE 754 规定

对于 32 位浮点数,最高的 1 位是符号位 S,接着的 8 位是指数 E,剩下的 23 位为有效数字 M

image-20241206134320169

对于 64 位的浮点数,最高的 1 位是符号位 S,接着的 11位是指数 E,剩下的 52 位为有效数字 M

image-20241206134347387

对于有效数字 M 和指数 E,还有一些特别规定

对于 M

由于 M 是范围在 1 和 2 之间,计算机在保存 M 时,默认这个数的第一位总是 1,因此可以舍去,只保存后面的小数部分。而在读取M的时候,会自动加上这个 1,这样的话可以节省一位有效数字,扩大保存数字的范围

例 M 为 1.01 时,假如没有舍去 1,可以保存23位有效数字;将 1 舍去,只保存后面的 01,等于可以保存 24 位有效数字

对于 E

由于 E 为一个无符号整数(unsigned int)没有负数,但浮点型数据的指数是可以为负的。因此存储 E 的真实值必须加上一个中间数 127(32 位平台下),这样若本身 E 为正,使用时减去 127 即可;若 E 本身为负,加上 127 为正数可以存储,使用时减去 127 即可

当 E 不全为 0 或不全为 1

指数 E 计算值减去 127(或 1023)得到真实值, 再将有效数字 M 前加上第一位的 1

当 E 全为 0

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

注 : 有效数字 M 不再加上第一位的 1,因为当 E 全为 0 时,该小数的范围在 (-1,1)之间

当 E 全为 1

如果有效数字 M 全为 0,表示 ± 无穷大(正负取决于符号位 S)

例存储十进制的 5.5

image-20241206134854999

补充知识:一个十进制的浮点数据怎么表示成二进制形式
核心要点:整数部分除 2 取余,小数部分乘 2 取整
举一个简单的例子:
178.125整数部分除 2 取余,直至得到 0
178 除 2 得 89,余 0
89  除 2 得 44,余 1
44  除 2 得 22,余 0
22  除 2 得 11,余 0
11  除 2 得 5, 余 1
5   除 2 得 2, 余 1
2   除 2 得 1, 余 0
1   除 2 得 0, 余 1
得到的余数从下往上排列得 10110010,即为 178 的二进制表示小数部分乘 2 取整
0.125 乘 2 得 0.25,整 0
0.25  乘 2 得  0.5,整 0
0.5   乘 2 得  1.0,整 1
得到的整数从上往下排列得 001,即 0.001 为 0.125 的二进制表示
合并上面的两点 178.125 的二进制表达方式为:10110010.011
练习题
/*
9 = 00000000 00000000 00000000 00001001
-> 转成 float 形式
->  0(S) 00000000(E) 00000000000000000001001(M)
->  (-1)^S * M (0.00000000000000000001001) * 2^E(-126)
-> %f 输出小数点后六位 0.000000把对应的 9 的内存空间转为 9.0 表示
-> 1001.0 = 1.0010 * 2^3
-> 转成 float 形式 
-> (-1)^S(0) * M (00100000000000000000000) * 2^E(130)
-> 0 10000010 00100000000000000000000
-> 二进制转十进制 1091567616
*/
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("n的值为 %d\n", n); //1091567616printf("*pFloat的值为 %f\n", *pFloat); //9.0
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com