剖析各种数据类型在内存中是怎样储存的?
家人们集美们,就是说咱们接下来会有一个大动作,那咱们先来浅读一下吧
推荐使用电脑端观看!手机端观感很差!
学习重点:
- 各种数据类型的具体介绍
- 整数在内存中存储:原码,反码,补码
- 大小端字节序
- 浮点数在内存中的存储
数据类型的介绍
-
各种数据类型
-
char //字符数据类型
char类型其实是属于整型家族中的,因为char类型实际存储的是ASCII码,ASCII为整数,所以char类型属于整形家族
-
short //短整型
-
int //整形
在早期的16位机器上是2个字节,在现在的32、64位机器上是4字节
-
long //长整型
C语言规定,sizeof(long)>=sizeof(int)
-
long long //更长的整形
-
float //单精度浮点数
-
double //双精度浮点数
-
_Bool //布尔类型:专门用来判断真假的
但其实在C语言中,对于布尔变量用的不多,其实本质就是把int进行了重命名
使用布尔类型要引用头文件:
#include<stdbool.h>
举个例子:
#include <stdbool.h> #include<stdio.h> int main() { _Bool flag = true; if (flag)//flag为真时打印 { printf("hehe\n"); } return 0; }
-
-
类型的基本归类
-
整形家族:
char
-
unsigned char
-
signed char
char到底是unsigned char还是signed char是取决于编译器的,常见的编译器中char是signed
char的
short
- unsigned short
- signed short
short是signed short
int
- unsigned int
- signed int
long
- unsigned int
- signed int
long是signed long
-
注:无符号数不应该用%d打印,应该用==%u==打印
举个例子:
#include<stdio.h> int main() { //正数:打印用%d %u都没问题 unsigned int num = 10; printf("%d\n", num); printf("%u\n", num); //负数:打印用%d没问题,用%u会错误 unsigned int n = -10; printf("%d\n", n); printf("%u\n", n); return 0; }
-
浮点数家族:
- float 单精度浮点数 4个字节
- double 双精度浮点数 8个字节
-
构造类型(自定义类型)
-
数组类型
数组的类型:去掉数组名(可以通过更改数据类型,元素个数来改变数据类型)
-
结构体类型 struct
-
枚举类型 enum
-
联合类型 union
-
-
指针类型
-
int* pi;
-
char* pc;
-
float* pf;
-
void* pv;
空类型:void表示空类型,即无类型
举个例子:
//返回值类型为空 参数为空 指针类型为void* void test(void) { void* p; } int main() { test(); return 0; }
-
-
整数在内存中的存储
-
原码 反码 补码
-
原码 反码 补码都由符号位 数值位组成
-
符号位:0正1负
-
数值位:
- 原码:直接将二进制按照正负数的形式翻译成二进制就可以。
- 反码:将原码的符号位不变,其他位依次按位取反就可以得到了。
- 补码:反码+1就得到补码。
-
正数的原反补相同,负数的原反补需要计算
-
在内存中其实存放的是补码
原因:
接下来,我们讲解几个例子
例子1:内存在VS2019怎么进行查看
-
按下键盘上的F10开始调试(部分笔记本用户需要按住Fn+F10,不过你也可以把Fn键锁上,方法:Fn+Esc,这时候你的Fn键就被锁了,直接按F10开始调试)
-
调试->窗口->内存->随便选择一个
-
输入&a,然后回车
-
我们需要关注的区域
例子2 :15与-15的原码 反码 补码
例子3 :10和-10的原码 反码 补码
-
-
大小端字节序
-
什么是大端小端:
-
举例:
int a = 1; ->0x 00 00 00 01
-
一道笔试题:来自百度2015年系统工程师笔试题
请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。
大端字节序存储: 当一个数据的低字节的数据存放在高地址处,高字节序的内容放在了低地址处,这种存储方式就是大端字节序存储
小端字节序存储:当一个数据的低字节的数据存放在低地址处。高字节序的内容,放在了高地址处,这种存储方式就是小端字节序存储
代码实现:
思路:
#include<stdio.h> int check_sys() { int a = 1; char *p = (char*)&a;//char类型的指针一次能访问一个字节,访问一次就是访问了第一个字节的内容 if (1 == *p) return 1;//小端 else return 0;//大端 } int check_sys() { int a = 1; char* p = (char*)&a; return *p; } int check_sys() { int a = 1; return *(char*)&a;//強转之后解引用,解引用访问一个字节 } int main() { int ret = check_sys();//返回1是小端,返回是大端 if (1 == ret) { printf("小端\n"); } else { printf("大端\n"); } return 0; }
-
-
八道练习题,让你直接起飞,芜湖~
-
下面程序输出什么?
#include <stdio.h> 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; }
-
下面程序输出什么?
#include <stdio.h> int main() { char a = -128; printf("%u\n", a); return 0; }
-
下面程序输出什么?
#include <stdio.h> int main() { char a = 128; printf("%u\n",a); return 0; }
-
下面程序输出什么?
#include <stdio.h> int main() { int i = -20; unsigned int j = 10; printf("%d\n", i + j); //按照补码的形式进行运算,最后格式化成为有符号整数 return 0; }
-
下面程序输出什么?
#include <stdio.h> int main() { unsigned int i; for (i = 9; i >= 0; i--) { printf("%u\n", i); } return 0; }
-
下面程序输出什么?
#include <stdio.h> int main() { char a[1000]; int i; for (i = 0; i < 1000; i++) { a[i] = -1 - i; } printf("%d", strlen(a)); return 0; }
-
下面程序输出什么?
#include <stdio.h> unsigned char i = 0; int main() { for (i = 0; i <= 255; i++) { printf("hello world\n"); } return 0; }
-
下面程序输出什么?
#include <stdio.h> 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; }
-
-
经历了这死亡八道题,你的表情是如何呢?是 ฅ՞•ﻌ•՞ฅ?是 (-̩̩̩-̩̩̩-̩̩̩-̩̩̩-̩̩̩___-̩̩̩-̩̩̩-̩̩̩-̩̩̩-̩̩̩)?是 Σ(-`Д´-ノ;)ノ?还是 ∑(O_O;)呢?选择你的答案吧。
浮点型在内存中的存储
常见的浮点数:
- 字面型浮点数:3.14159
- 1E10->1.0*10^10
- 浮点数家族包括: float、double、long double 类型。
- 浮点数表示的范围:float.h中定义
在VS2019中输入#include<float.h>,然后右键->转到文档
从第85行开始就有关于float类型的具体内容了
-
一个浮点数存储的例子
#include<stdio.h> int main() { int n = 9; float* pFloat = (float*)&n; printf("n的值为:%d\n", n); printf("*pFloat的值为:%f\n", *pFloat); *pFloat = 9.0; printf("num的值为:%d\n", n); printf("*pFloat的值为:%f\n", *pFloat); return 0; }
运行结果:
震惊!第一个打印的*pFloat怎么不是9.000000?第二个人打印的n怎么不是9.0?
那么其实整数与浮点数的存储方法不同
-
浮点数的存储规则
浮点数无原码 反码 补码
-
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:
(-1)^S * M * 2^E
(-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。
M表示有效数字,大于等于1,小于2。
2^E表示指数位。举个例子:5.5写成IEEE754形式
-
IEEE 754规定:
-
对于32位(float)的浮点数,最高的1位是符号位S,接着的8位是指数E,剩下的23位为有效数字M。
-
对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。
-
IEEE 754对有效数字M和指数E,还有一些特别规定。
- 存进去:
- 有效数字M:前面说过, 1≤M<2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中xxxxxx表示小数部分。
IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。 - 指数E:IEEE 754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。
- 有效数字M:前面说过, 1≤M<2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中xxxxxx表示小数部分。
- 拿出来:
- E不全为0或不全为1:指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。
- E全为0:浮点数的指数E等于==1-127(或者1-1023)==即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。
- E全为1:如果有效数字M全为0,表示±无穷大(正负取决于符号位S)(不做深入研究)
- 存进去:
-
-
-
刚开始的例子讲解:
通过这一篇文章,相信你已经对数据在内存中的存储已经得心应手了,下一篇文章,我们会进行高难度指针的探索,希望你不要害怕做好准备!加油 !
喜欢这篇文章的话,请一件三连!感谢!!!