静态网站的短处,厦门专门建设网站的公司,wordpress搜索过滤,网站备案是 备案空间吗引言
数据是程序运行的核心。当我们用C语言编写程序时#xff0c;我们实际上是在操纵内存中的数据。这些数据在内存中是如何储存的#xff0c;今天我们就来学习这些内容。 基本数据类型
1.整型 int: 基本整型#xff0c;通常占用4个字节 short: 短整型#xff0c;通常占用… 引言
数据是程序运行的核心。当我们用C语言编写程序时我们实际上是在操纵内存中的数据。这些数据在内存中是如何储存的今天我们就来学习这些内容。 基本数据类型
1.整型 int: 基本整型通常占用4个字节 short: 短整型通常占用2个字节 long: 长整型通常占用4个字节 long long: 更长的整型8个字节 signed: 有符号整型可以表示正数、负数和零 unsigned: 无符号整型只能表示非负整数 2.浮点型 float: 单精度浮点型通常占用4个字节 double: 双精度浮点型通常占用8个字节 long double: 扩展双精度浮点型精度高于double占用的字节数也更多有 16 字节、12 字节、8 字节其中 16 字节占大多数 3.字符型 char: 字符型通常占用1个字节 4.复合数据类型 数组 结构体 联合体 枚举 其中结构体、联合体和枚举我将会单独写一篇文章为大家介绍敬请期待~
5.指针类型 //32位环境下指针变量大小 4 //64位环境下指针变量大小 8 char*字符指针 short*短整型指针 int*整型指针 long*长整型指针 long long*更长类型指针 float*单精度浮点数指针 double*双精度浮点数指针 void*空类型指针 6.void类型 void 类型通常用于表示没有返回值或没有参数的函数或者用于声明一个指针该指针不指向任何具体类型的数据 整数在内存中的储存
1.原码、反码、补码
在之前学习位操作符时我们就有学习到
整数的二进制表示方式有三种即原码、补码和反码
原码、反码、补码是计算机中用于表示和处理有符号整数的三种编码方式
三种表示方法均由符号位和数值位组成最高位为符号位
符号位用0表示正数用1表示负数
正数的原码反码补码均相同
负数的原码反码补码均不同
原码直接将数值按照正负数的形式转换为二进制得到的就是原码
反码原码符号位不变其他位按位取反得到的就是反码
补码反码1得到的就是补码
对于整型来说数据存放其实存放的是补码
原因是1.使用补码可以将符号位和数值位统一处理 2.补码与原码相互转换其运算过程是相同的不需要额外的硬件电路
也可以看看我之前写的文章或许会更有利于理解
C语言——位操作符详解
2.signed和unsigned
2.1 signed
signed表示有符号的整数类型。有符号整数的表示方法是采用二进制补码最高位最左边的一位是符号位用于表示正负0表示正1表示负
C语言中的有符号整数类型包括signed char、signed short、signed int和signed long它们的取值范围和精度取决于编译器和平台的实现。例如signed char的取值范围是-128到127占用1个字节8位的存储空间
2.2 unsigned
与signed相对unsigned表示无符号的整数类型。无符号整数只能表示非负数包括零其范围从零到正的最大值
unsigned整数在计算机中的存储方式与有符号整数有所不同。对于无符号整数所有的位都用于表示数值没有专门的符号位
在C语言中常见的无符号整数类型有unsigned char、unsigned short、unsigned int和unsigned long。它们的取值范围通常是从0到某个正的最大值这个最大值取决于整数类型占用的位数 大小端
我们来看一段代码并试着通过调试查看一下内存 int main()
{int a 0x11223344;return 0;
}
我们可以看到a中的0x11223344这个数字似乎是按照字节为单位倒着排序的这是为什么呢
其实这是小端字节序系统的特性
接下来我们来学习一下大小端
1.什么是大小端
大小端是计算机系统中关于字节序的一个术语它描述的是多字节数据在内存中的存放顺序。具体来说大小端涉及到如何排列一个数值的多个字节。 大端高位字节存放在内存的低地址端低位字节存放在内存的高地址端。这种存储方式符合人类常规的数值读写习惯即先读高位字节再读低位字节。 小端低位字节存放在内存的低地址端高位字节存放在内存的高地址端。这种存储方式在计算机内部处理数据时较为常见因为大多数计算机电路都是先处理低位字节再处理高位字节从而提高处理效率。 来看个图
2.为什么有大小端
这是因为在计算机系统中我们是以字节为单位的每个地址单元都对应着⼀个字节⼀个字节为8 bit 位但是在C语言中除了8 bit 的 char 之外还有16 bit 的 short 型32 bit 的 long 型要看 具体的编译器另外对于位数大于8位的处理器例如16位或者32位的处理器由于寄存器宽度大于⼀个字节那么必然存在着⼀个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。
大小端各有其优缺点适用于不同的场景。例如小端模式在处理数值计算时更为高效因为CPU在进行数值计算时低位与低位相加只需要顺序读取内存地址。而大端模式在判断数据的正负和大小方面更为方便因为数据的符号位位于其对应的内存地址的第一个字节。
3.判断大小端
方法1
最简单的方法自然是直接通过调试查看内存啦
方法2
通过代码判断
我们需要取出n的地址的第一位怎么样才能取出第一位呢
我们可以用 char* 取出n的地址的第一位
在小端字节序的计算机中int类型的1在内存中的表示是0x01 00 00 00假设int是4字节。因此通过char指针解引用得到的第一个字节的值是0x01即十进制的1
而在大端字节序的计算机中int类型的1在内存中的表示是0x00 00 00 01。通过char指针解引用得到的第一个字节的值是0x00即十进制的0
代码实现如下 int main()
{int n 1; //0x00 00 00 01//小端01 00 00 00//大端00 00 00 01if (*(char*)n 1) //char* 为一个字节printf(小端\n);elseprintf(大端\n);return 0;
} 整型截断
1.什么是整型截断
整型截断是指在将一个字节大的整型数据赋值给一个字节小的整型变量时发生的数据丢失现象
整型数据的存储大小是固定的例如char类型通常占用1个字节int类型通常占用4个字节。当我们试图将一个int类型的值赋给一个char类型的变量时由于char类型的存储空间较小无法容纳int类型的全部数据因此会发生截断
2.例子
来看个示例 int main()
{int a -1;char b a;printf(%d, b);return 0;
} a的原码10000000000000000000000000000001
a的反码11111111111111111111111111111110
a的补码11111111111111111111111111111111
由于b是char类型的只有1个字节占8个比特位当a赋值给b时会出现整型截断最低8位保留最高24位舍弃
b的补码11111111
b的反码11111110
b的原码10000001
因此b等于-1 整型提升
1.什么是整型提升
整型提升是一种隐式类型转换机制。C语言中字节数少于整型字节数的数据类型在进行整型运算时该类型的数据会被默认转为整型数据
其中该类型的数据被转化为整型数据的过程就称为整型提升
2.例子
那数据是如何进行整型提升的
有如下两条规则 如果是无符号数则高位直接补0 如果是有符号数则高位全补符号位 例子1 char a1; 补码00000001
有符号数符号位为0整型提升后为00000000 00000000 00000000 00000001
例子2 char b-1; 补码 11111111
有符号数符号位为1整型提升后为11111111 11111111 11111111 11111111
例子3 unsigned c-1; 补码11111111
无符号数高位全部补0整型提升后为00000000 00000000 00000000 11111111 练习题
1.练习题1
int main()
{char a -1;// a的原码10000000000000000000000000000001// a的反码11111111111111111111111111111110// a的补码11111111111111111111111111111111signed char b -1;unsigned char c -1;//a、b、c均存储为11111111printf(a%d b%d c%d, a, b, c);//发生整型提升//a、b有符号整型提升为11111111111111111111111111111111//c无符号整型提升为00000000000000000000000011111111return 0;
}
输出结果为 a-1 b-1 c255 2.练习题2
int main()
{char a -128;//原码10000000000000000000000010000000//反码11111111111111111111111101111111//补码11111111111111111111111110000000//发生整型截断10000000printf(%u, a);//发生整型提升11111111111111111111111110000000//%u打印无符号整型4294967168return 0;
}
输出结果为 4294967168 3.练习题3
int main()
{char a[1000];int i 0;for (int i 0; i 1000; i){a[i] -1 - i;//有符号char最小值为11111111 --127//C语言特别规定10000000--128//由于最高位为符号位//所以最大值为01111111-127}printf(%d, strlen(a)); //strlen以\0(ASCII为0)为结束标志//当i128时//-129的补码11111111111111111111111101111111//发生整型截断//01111111-127//因此a[i]储存的数据为-1、-2、-3......-128、127、126......1、0//长度127128255return 0;
}
输出结果为 255 为了更方便我们理解我们可以尝试用画图来直观的看一下 补充图中的数字均以补码形式表示
循环会从-1开始逆时针旋转直到遇到0为止
所以a[i]储存的数据为-1、-2、-3......-128、127、126......1、0
长度127128255
4.练习题4
int main()
{unsigned char i 0;for (i 0; i 255; i){printf(hello world\n);}//由于i是无符号字符类型//最小值为0最大值为255//i在递增到255时再1又会回到0//因此代码会循环打印hello worldreturn 0;
}
输出结果 循环打印 hello world 5.练习题5
int main()
{unsigned int i;//i为无符号整数for (i 9; i 0; i--){printf(%u , i);}//先打印9 8 7 6 5 4 3 2 1 0//然后到-1//由于i是unsigned int类型不能表示负数//当i递减到0并执行i--操作时它不会变成-1//而是变成unsigned int能够表示的最大值//即2^32-1//因此循环条件i0始终为真//最终导致死循环return 0;
}
输出结果为 9 8 7 6 5 4 3 2 1 0......死循环 浮点数在内存中的存储
1.浮点数的存储规则
根据国际标准IEEE电气和电子工程协会754任意一个二进制浮点数V可以表示成如下形式 V (−1) ^S*M *2^E • (-1)^S 表示符号位当S0V为正数当S1V为负数 • M表示有效数字M是大于等于1小于2的 • 表示指数位 2.例子
为了方便我们理解我们来看个例子
十进制的5.0写成二进制是101.0相当于1.01×2^2
那么根据V的格式我们可以得知
S0M1.01E2
IEEE 754规定
对于32位的浮点数最高的1位存储符号位S接着的8位存储指数E剩下的23位存储有效数字M 对于64位的浮点数最高的1位存储符号位S接着的11位存储指数E剩下的52位存储有效数字M 3.浮点数的存与取
3.1 浮点数的存储过程
IEEE 754 对有效数字M和指数E还有⼀些特别规定 前⾯说过 1≤M2 也就是说M可以写成 1.xxxxxx 的形式其中 xxxxxx 表示小数部分。 IEEE 754 规定在计算机内部保存M时默认这个数的第⼀位总是1因此可以被舍去只保存后面的xxxxxx部分。比如保存1.01的时候只保存01等到读取的时候再把第⼀位的1加上去。这样做的目的是节省1位有效数字。以32位浮点数为例留给M只有23位将第⼀位的1舍去以后等于可以保存24位有效数字
指数E是一个无符号整数unsigned int
如果E为8位它的取值范围为0~255如果E为11位它的取值范围为0~2047。但是我们知道科学计数法中的E是可以出现负数的所以IEEE 754规定存⼊内存时E的真实值必须再加上 ⼀个中间数对于8位的E这个中间数是127对于11位的E这个中间数是1023。比如2^10的E是10所以保存成32位浮点数时必须保存成10127137即10001001
3.2 浮点数的取出过程
指数E从内存中取出可以分为三种情况
E不全为0或不是1
这时浮点数采用以下规则表示指数E的计算值减去127或者1023得到真实值再将有效数字M前加上第一位的1
比如0.5的二进制形式位0.1由于规定了正数部分必须为1则需要将小数点右移1位则为1.0×2^-1其阶码为-1127中间值126表示为01111110而尾数1.0去掉整数部分为0补充0到23位00000000000000000000000其二进制表示位 0 01111110 00000000000000000000000 E全为0
这时浮点数的指数E等于1-127或者1-1023即为真实值有效数字M不再是加上第一位的1而是还原成0.xxxxxx的小数。这样做是为了表示±0以及接近于0的很小的数字
E全为1
255 - 127 128 或 2047 - 1023 1024 与第二点相反这时这个数无穷大
这时如果有效数字M全为0表示±无穷大正负取决于符号位S
4.例题解析
int main()
{int n 9;float* pfloat (float*)n;printf(n的值为%d\n, n);printf(*pfloat的值为%f\n, *pfloat);*pfloat 9.0;printf(n的值为%d\n, n);printf(*pfloat的值为%f\n, *pfloat);return 0;
}
输出结果为 n的值为9 *pfloat的值为0.000000 n的值为1091567616 *pfloat的值为9.000000 我们来分析一下
1.将9的二进制序列按照浮点数的形式拆分 9的补码为0000 0000 0000 0000 0000 0000 0000 1001 写成浮点数表示的形式0 00000000 00000000000000000001001 我们可以得到符号位S0指数位E00000000有效数字位M00000000000000000001001 由于指数E全为0符合E全为0的情况因此浮点数V写成 V(-1)^0× 0.00000000000000000001001×2^(-126)1.001×2^(-146) 显然V是一个很小十分接近0的数所以用十进制小数表示就是0.000000
2.n被改为浮点数9.0以整数的方式打印遵循整型的存储方式 浮点数9.0等于二进制的1001.0 用V表示V1.001×2^3 所以9.0(-1)^0 ×1.001×2^3 我们可以得到符号位S0有效数字M等于001后面补充20个0凑满23位指数E3127130 即10000010 因此浮点数V写成二进制形式为 0 10000010 00100000000000000000000 这个32位的二进制数被当作整数进行解析时就是整数在内存中的补码 以整数形式打印就是1091567616
3.浮点数以浮点数形式打印输出结果为9.000000 结束语
磨蹭了好久终于是把数据在内存中的存储的内容写完了。
希望看到这里的友友们能点赞收藏关注
十分感谢