数据类型
1,整形:(默认有符号数)
在内存中存的是其对应二进制补码
char(存在内存中是其ASCLL码)
short(短整型)
int(整形)
long(长整形)
2,浮点型:
float
double
long double(不是所有编译器都支持)
3,构造类型(自定义类型)
数组
结构体(struct)
枚举(enum)
联合体(union)
4,指针类型
void*
float*
short*
char*
5,空类型
void
一.整型在内存的存储
首先我们要知道
整数在计算机中存的是它的补码
原码——>反码——>补码
三者的运算规则为
原码按位取反(符号位不变)——>反码。
反码+1——>补码。
在32位计算机中首位是符号位
表示正负不表示大小。
首位为1表示负数,首位为0表示正数。
eg(都是32位)
10000000000000000000000000000001表示-1
00000000000000000000000000000001表示1
对于正整数规定,它的原码,反码,补码都相同可以直接用。
对于负数计算机存的补码不等,要转成原码才可以直到其大小。
eg:
1 1 1 1 1…1 1(32位)(符号位1说明是负数)补码
1 1 1 1 1…1 0(32位)(补码-1到反码)
1 0 0 0 0…0 1(32位)(符号位不变其余位按位取反到原码)
原码首位为1说明是负数,发现值数字-1。
-----------------------------------------------------------------
整形在内存中存储补码的原因:
使用补码可以将符号位和数值域统一处理,原反补的转化运算过程相同,不用增添其他的计算原件。
eg:
计算10-1的值
10的二进制位原码与反码,补码相同
0 0 0 0 0 0… 1 0 1 0
-1在内存中存储的二进制位为
1 1 1 1 1 1… 1 1 1 1
相加为补码与补码相加
0 0 0 0 0 0…0 1 0 1 0
1 1 1 1 1 1…1 1 1 1 1
观察i在内存中的储存方式
#include<stdio.h>
int main()
{
int i = 1;
return 0;
}
而1的16进制为
00 00 00 01
发现这里是“倒着存数据”
整形存储方式有多种,常见的有
大端字节序储存与小端字节序储存
1.大端字节序储存:
(把一个数据的低位字节内容存在高地址处)
2.小端字节序储存:
(把一个数据的低位字节内容存在低字节处)
eg:
设 int a变量的地址为:0x 12 34 56 78(四个字节)
应用:
1,判断机器的存储方式
#include<stdio.h>
int main()
{
int i = 1;
char* p =(char*) &i;
if (*p == 1)
{
printf("该计算机是小端存储");
}
else
{
printf("该计算机是大端存储");
}
return 0;
}
这里i=1的16进制位为:
0x 00 00 00 01(四个字节)
char*的访问权限只有一个字节
如果p为1,说明先储存的是二进制的低字节位,为小端字节序储存
如果p为0,说明先储存的是二进制的高字节位,为大端字节序储存
整形提升
eg:
#include<stdio.h>
int main()
{
char a = 3;
char b = 137;
char c = a + b;
printf("%d",c);
return 0;
}
发现这里并不是我们想的140,
这是因为char类型大小是一个字节,而一个整形是4个字节
char—8个比特位,
int—32个比特位,
整型提升是按照符号位进行提升的符号位就是二进制位的首位
+号操作符操作的是int型,所以char类型先补到32位
a=0 0 0 0 0 0 1 1(补位,符号位为0)
a=0000…0 1 1(32位)
b= 1 1 1 1 1 1 1 1(补位)
b=0 0 0 0 0 0 1 1 1 1 1 1 1(32位)
a+b=0 0…1 0 0 0 0 0 1 0(32位)
c=a+b(c是字符类型,只能存8位,所以会发生截断)
c=1 0 0 0 0 0 1 0
printf("%d")所以c还要进行整形提升
c的符号位是1,所以补1到32位
c整型提升后
1 1 1 1 1 1…1 0 0 0 0 0 1 0(32位)(补码)
1 1 1 1 1 1…1 0 0 0 0 0 0 1(32位)(补码)
1 0 0 0 0 0…0 1 1 1 1 1 1 0(32位)(原码)
符号位为1说明是负数
翻译原码得-116
有些数字如果是无符号位数,整形提升补零
整型提升只发生在有二进制位且大小小于int的时候
发生了整型提升。
当从int转换成比int大的类型时不会出现这种情况,不会截断
有符号数与无符号数
#include<stdio.h>
int main()
{
char a = -1;
unsigned char b = -1;
printf("a=%d b=%d",a,b);
}
输出结果:
a发生了截断又发生了整形提升不在赘述。
b的类型是unsigned char,二进制位为
1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0…1 1 1 1 1 1 1 1(32位)=255
无符号数整形提升补0
有符号char:
范围为-128~127。
当超过取值范围时
(无符号)char:
最大值为:1 1 1 1 1 1 1 1=255
最小值为:0
超过范围时:
255+1=0
eg:
#include<stdio.h>
#include<string.h>
int main()
{
char arr[1000];
for (int i = 0; i < 1000; i++)
{
arr[i] = -1 - i;
}
printf("%d",strlen(arr));
return 0;
}
strlen读取到0会停止
127+128=255
其他无符号整形类似
二.浮点数在内存中的存储
在讨论这个问题前先看一个经典题目
#include<stdio.h>
int main()
{
int n = 9;
float* p = (float*)&n;
printf("%d\n",n);
printf("%f\n",*p);
*p = 9.0;
printf("%d\n",n);
printf("%f\n",*p);
}
输出结果:
当n是整形时,以浮点数打印和以整形打印的数字不同。当n是浮点型时,以整形打印的和以浮点数打印的数字不同。
说明整形与浮点型的存储方式不同。
浮点数的储存这里以10.5为例
10的二进制有效位为1 0 1 0。
0.5所在的二进制位的权重为2^-1
综上10.5=1 0 1 0 . 1=(-1) ^ 0 * 1 . 0 1 0 1 * 2 ^ 3;
规定:
1,任意的二进制浮点数可以表示为:
(-1)^ S * M 2 ^ E*
其中
S是符号位,正浮点数为0;负浮点数为1。
M为有效数字,范围为1~2。
E为指位数。
2,对于单精度浮点型float:
S 占 1bit, E 占 8bit,M占23bit
其中M一定为1.XXXXXX,所以1不存在内存中
23bit全部存小数位。
对于双精度浮点型double:
S 占 1bit,E 占 11bit,M占52bit
其中M一定为1.XXXXXX,所以1不存在内存中
23bit全部存小数位。
S _E _M放的顺序为
3,规定E是无符号数
对于单精度float:
E=真实e+127;再化成二进制
eg: 10.5=1 0 1 0 . 1=(-1) ^ 0 * 1 . 0 1 0 1 * 2 ^ 3
E=e+127=3+127=130
对于双精度double:
E=真实e+1023;再化成二进制
(注意特殊情况)
1,当E的二进制位为全0时
E=e真实+127;
e真实特别小,很接近0,这时规定有效数字
M (1.XXXXXXXXXXX)不写小数点前的1,
且规定E=1-127= -126
用来表示这个数字很小
—————————————————————
2,当E的二进制位为1时(这里只讨论单精度)
E=e真实+127=255
e真实很大,这个数字是 ± 无穷
这是如果M为全0,表示无穷大,正负看S。
这里不再更深的研究。
这时再来看例题
#include<stdio.h>
int main()
{
int n = 9;
float* p = (float*)&n;
printf("%d\n",n);
printf("%f\n",*p);
*p = 9.0;
printf("%d\n",n);
printf("%f\n",*p);
}
9的二进制位为:
0 0 0 0 0 0 0 0…1 0 0 1(32位)
当以浮点型打印时二进制会被拆分
同理浮点数9.0
9.0=(-1)^0 *1.001 * 2 ^3
S=0 M=1.001 E=127+3=130
所以最后打印的结果为