目录
一,类型介绍
在C语言中,常用的数据类型有7种
内存占用大小/Byte | ||
char | 字符数据类型 | 1 |
short | 短整型 | 2 |
int | 整型 | 4 |
long | 长整型 | 4 |
long long | 长长整型 | 8 |
float | 单精度浮点型 | 4 |
double | 双精度浮点型 | 8 |
类型归类:
整型家族 | 浮点型家族 | 指针家族 | 构造家族 | 空类型 | |
char | unsigned char signed char | float | 整型指针(char*,int*...) | struct | void |
short | unsigned short signed short | double | 浮点型指针(float*,double*) | enum | |
int | unsigned int signed int | 结构体指针(eg:struct s*) | union | ||
long | unsigned long signed long | 空指针(void*) | 数组 | ||
long long | unsigned long long signed long long | 数组指针(eg:int (*a)[2] ) |
好了,我们大概知道了数据的类型,那么这些数据的类型在内存中是如何存储的呢?存储的大小是怎么样的呢?我们用代码一探究竟吧。
二,数据储存的大小
整型,浮点型
第一个表中有写到,常用类型的存储大小,那么我们用代码验证一下:
此外,那么其他类型的存储大小是多少呢?我们再用代码验证一下:
指针
32位状态: 64位状态:
可以看到,不同状态下的指针,大小也不同,且无论什么类型,都是一样的大小,
由此可得:指针的大小只与平台大小有关
32位 | 64位 |
4byte | 8byte |
枚举型
枚举型的存储大小在C语言中默认是4个字节,一个整型的大小。如下图(vs2022):
类型 | 大小(byte) |
enum | 4 |
值得注意的是:如果赋给的值大于2^32-1则会产生截断
由于结构体,联合体的内存占用不是固定值,有内存的对齐方式,所以在下篇博客进行介绍。
三,存储方式
我们知道了数据在内存存储的大小,也知道计算机是一01的二进制位进行计算的,但是数据在内存里是如何存放的呢,他们之间又有什么关系呢,下面我们来一一探解。
关系:任何数据在内存中都是以二进制的形式存储的。
整型
呈现方式:原码呈现
存储方式:补码存储,补码计算,以原码来打印
原因:使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
例子:
(内存里以16进制显示)
我们用几道题来加深一下对他们的存储方式的理解
下面程序输出什么:
1.
#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; }
2.
int main() { char a = -128; printf("%u\n", a); return 0; }
3.
int main() { char a = 128; printf("%u\n", a); return 0; }
4.
int main() { int i = -20; unsigned int j = 10; printf("%d\n", i + j); return 0; }
5.
int main() { unsigned int i; for (i = 9; i >= 0; i--) { printf("%u\n", i); } return 0; }
6.
int main() { char a[1000]; int i; for (i = 0; i < 1000; i++) { a[i] = -1 - i; } printf("%d", strlen(a)); return 0; }
7.
unsigned char i = 0; int main() { for (i = 0; i <= 255; i++) { printf("hello world\n"); } return 0; }
题解1.
2.
3.
4.
5.
6.
7.
以上解题方法都是依照编译器底层工作逻辑来计算的,其实像第四题,只要数值不超过类型的规定范围,就可以按照直接相加减来计算。
好了,我们知道了整型在内存里是一二进制补码的形式存储和计算的,以原码的形式打印的,那么浮点型又是如何呢?是否一样,让我们继续了解。
浮点型
浮点型的存储有公式可循:(-1)^S*M*2^E
十进制 二进制 公式化 S,M,E 5.5 101.1 (-1)^0*1.011*2^2 S = 0,M = 1.011,E = 2 10.25 1010.01 (-1)^0*1.01001*2^3 S = 0,M = 1.01001,E = 3 -7.75 111.11 (-1)^1*1.1111*2^2 S = 1,M = 1.1111,E = 2 因为在小数部分中,十进制转换为二进制存在不精准的情况。所以有些浮点型小数在内存无法精确保存。
得到S,M,E后,则内存就对此进行对应存储。
在存储之前,IEEE 754规定:
- 1≤M<2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中xxxxxx表示小数部分。在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。
- 因为E可以取负数,所以规定在加一个修正值,32位平台下E加上127,64位平台下E+1023,确保E+127/1023在内存里存储的为无符号数。
存储规则如下:
(32位平台下E+127)
S(1bit) | E(8bit)+127 | W(23bit) | |
5.5(S = 0,M = 1.011,E = 2) | 0 | 10000001 | 01100000000000000000000 |
-7.75(S = 1,M = 1.1111,E = 2) | 1 | 10000001 | 11111000000000000000000 |
10.25(S = 0,M = 1.01001,E = 3) | 0 | 10000010 | 01001000000000000000000 |
我们可以验证一下:
由于vs2022内存是小端存储,所以
内存存储方式 | |||
5.5 | 0100 0000 1011 0000 0000 0000 0000 0000 | 40 B0 00 00 | 00 00 B0 40 |
-7.75 | 1100 0000 1111 1100 0000 0000 0000 0000 | C0 FC 00 00 | 00 00 FC C0 |
10.25 | 0100 0001 0010 0100 0000 0000 0000 0000 | 41 24 00 00 | 00 00 24 41 |
注意:E全为1和全为0的两种特殊情况
00000000 | E取-126,M取.xxxx,该浮点数大小为0 |
11111111 | 该浮点数大小为正/负无穷级数 |
好的,以上浮点型的存储规则就介绍的差不多了,我们来道题巩固一下知识点。
//打印的数是什么
int main()
{
int a = 10;
float* pa = (float*) & a;
printf("%d %f\n", a,*pa);
*pa = 10.25;
printf("%d %f\n", a, *pa);
return 0;
}