数据类型介绍目录
数据类型介绍
类型的基本归类
整型家族
浮点数家族
构造类型
指针类型
空类型
整型在内存中的存储
二进制
原码、反码、补码
大小端字节序
练习
浮点型在内存中的存储
一个例子
浮点数的存储规则
存储
读取
C语言的基本内置类型
名称 | 解释 | 占据空间(单位字节) |
char | 字符 | 1 |
short | 短整型 | 2 |
int | 整型 | 4 |
long | 长整型 | 4(32位)/8(64位) |
long long | 长长整型 | 8 |
float | 浮点型 | 4 |
double | 双精度浮点型 | 8 |
类型的意义:
一、决定了开辟的内存空间的大小。
二、决定了看待内存空间的角度(存入和读取数据的方式)。
类型的基本归类 整型家族名称 | 解释 | 备注 |
char | 字符 | 标准为定义有符号或则无符号,具体看编译器 |
signed char | 有符号 | |
unsigned char | 无符号 | |
short | 短整型 | 默认有符号 |
signed short | 有符号 | |
unsigned short | 无符号 | |
int | 整型 | 默认有符号 |
signed int | 有符号 | |
unsigned int | 无符号 | |
long | 长整型 | 默认有符号 |
signed long | 有符号 | |
unsigned long | 无符号 | |
long long | 长长整型 | 默认有符号 |
signed long long | 有符号 | |
unsigned long long | 无符号 |
signed与unsigned的区别:
数据在内存中是二进制储存。
浮点数家族名称 | 解释 | 备注 |
float | 单精度浮点型 | 精度低,表示范围小 |
double | 双精度浮点型 | 精度高,表示范围大 |
名称 | 解释 | 备注 |
type [const_n] | 数组类型 | |
struct tag | 结构体类型 | |
enum tag | 枚举类型 | |
union tag | 联合体类型 |
名称 | 解释 | 备注 |
int * | 整型指针 | |
char * | 字符指针 | |
float * | 浮点指针 | |
void * | 空指针 | |
................. |
void类型表示空类型,通常用于函数返回值,函数参数,空指针。
整型在内存中的存储 二进制数据在内存中以二进制的形式存储,编译器下表现为十六进制。
原码、反码、补码正数的原码,反码,补码相同,负数的原码,反码,补码需要计算。
对整型来说,内存中存放的就是补码。
但是为什么使用补码呢?
原因有三:
一、使用补码,可以将符号位和数值位统一处理。
二、加法和减法可以同一处理(CPU只有加法器)。
三、补码与原码可以相互转换,同样使用取反加一的方式,不需要额外的硬件电路。
10的原码:0000 0000 0000 0000 0000 0000 0000 1010
10的反码:0000 0000 0000 0000 0000 0000 0000 1010
10的补码:0000 0000 0000 0000 0000 0000 0000 1010
-20的原码:1000 0000 0000 0000 0000 0000 0001 0100
-20的反码:1111 1111 1111 1111 1111 1111 1110 1011
-20的补码:1111 1111 1111 1111 1111 1111 1110 1100
补码相加:0000 0000 0000 0000 0000 0000 0000 1010
补码相加:1111 1111 1111 1111 1111 1111 1110 1100
相加结果:1111 1111 1111 1111 1111 1111 1111 0110(补码)
上面的运算符号位与数值域同一处理,同时10-20的运算可以看作10+(-20)直接使用加法。
解析成结果
补码:1111 1111 1111 1111 1111 1111 1111 0110
反码;1000 0000 0000 0000 0000 0000 0000 1001(除符号位取反)
原码:1000 0000 0000 0000 0000 0000 0000 1010(反码+1)
大小端字节序
内存开辟空间后,存入的数据该怎么储存呢?
实际上有两种储存的方法。
为什么会有大小端字节序呢?
这是因为计算机系统中,是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8比特,但是也有16比特和32比特,对于大于8比特的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题,因此就有了大小端字节序。
常用的X86结构式小端模式,而KEIL C51则为大端模式。
练习设计一个小程序来判断当前机器的字节序。
int check_sys()
{
int a = 1;
return *(char *)&a;
}
int main()
{
if (check_sys())
{
printf("小端\n");
}
else
{
printf("大端\n");
}
system("pause");
return 0;
}
以下代码输出什么
int main()
{
char a = -1;
signed char b = -1;
unsigned char c = -1;
printf("%d %d %d\n", a, b, c);
system("pause");
return 0;
}
过程
char标准未定义,VS下char默认为signed char
所以a,bc中的补码为;1111 1111
打印时,按照类型进行整型提升,所以a,b提升后的补码为:1111 1111 1111 1111 1111 1111 1111 1111
c是unsigned提升后为:0000 0000 0000 0000 0000 0000 1111 1111.
%d打印是有符号打印,所以a,b的结果为-1,c的结果为255。
以下代码运行什么
int main()
{
char a = -128;
printf("%u\n", a);
system("pause");
return 0;
}
-128原码:
1000 0000 0000 0000 0000 0000 1000 0000
-128反码:
1111 1111 1111 1111 1111 1111 0111 1111
-128补码:
1111 1111 1111 1111 1111 1111 1000 0000
截断存入a:
1000 0000
无符号类型打印
整型提升(char类型,俺符号位提升)补码:
1111 1111 1111 1111 1111 1111 1000 0000
打印
以下代码运行什么
int main()
{
char a = 128;
printf("%u\n", a);
system("pause");
return 0;
}
128原码:
0000 0000 0000 0000 0000 0000 1000 0000
128反码:
0000 0000 0000 0000 0000 0000 1000 0000
128补码:
0000 0000 0000 0000 0000 0000 1000 0000
截断存入a:
1000 0000
无符号类型打印
整型提升(char类型,俺符号位提升)补码:
1111 1111 1111 1111 1111 1111 1000 0000
打印
以下代码运行什么
int main()
{
int i = -20;
unsigned j = 10;
printf("%d\n", i + j);
system("pause");
return 0;
}
代码执行过程设计到类型转换
int类型转换成unsigned int类型
-20补码:
1111 1111 1111 1111 1111 1111 1110 1100
20的补码:
0000 0000 0000 0000 0000 0000 0000 1010
相加(注意相加后的数据是无符号类型)
1111 1111 1111 1111 1111 1111 1111 0110(补码)
但是打印是有符号打印所以是-10
以下代码运行什么
int main()
{
unsigned int i;
for (i = 9; i >= 0; i--)
{
printf("%u\n", i);
}
system("pause");
return 0;
}
for的表达式中,i是无符号类型,所以一直大于等于0,所以结果为死循环。
循环的内容为从9到0,再到i的类型不断减小的循环。
以下代码运行什么
int main()
{
char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d", strlen(a));
system("pause");
return 0;
}
char类型的取值范围是从-128-127。a[i]的值从-1,-2,-3.........0,之后继续循环。
strlen函数求的是数组中0元素之前的数,a[1000]中,0之前的数是255个。
浮点型在内存中的存储
浮点数就是小数,之所以叫浮点数是因为小数点可以移动。
浮点数家族包括:float,double,long double(C99定义)。
浮点数的具体取值范围在float.h中。
整数的具体取值返回在limits.h中。
那么浮点数与整数除了小数点的区别外,还有什么不同呢?
一个例子int main()
{
int i = 9;
float *pi = (float *)&i;
printf("1=%d\n", i);
printf("2=%f\n", i);
*pi = 9.0f;
printf("3=%d\n", i);
printf("4=%f\n", i);
system("pause");
return 0;
}
上面的例子中
1=9,2=0.000000,内存中存储的明明是整型9,为什么浮点打印时会变成0.000000而不是9.000000呢?
解引用*pi并赋值9.0后,
3=1091567616,4=9.000000,内存中明明存储的是9.0,为什么整型打印会打印出1091567616呢?
原因是浮点数的存储规则与整数不同。
浮点数的存储规则SME存储规则。
IEEE754中规定,浮点数以SME的方式存储,所谓的SME是指任何一个浮点数V都可以表示成下面的形式:
V=(-1)^S * M *2^E。
其中(-1)^S表示符号位,当S=0是,V为正数,当S=1时,V为负数。
M表示有效数字,取值范围大于等于1,小于2。
2^E表示指数位,2是因为使用的是二进制。
举个例子:
浮点数 | 5.0 |
二进制 | 101.0 |
公式 | (-1)^0*1.01*2^2 |
S | 0 |
M | 1.01 |
E | 2 |
浮点数 | 9.5 |
二进制 | 1001.1 |
公式 | (-1)^0*1.0011*2^3 |
S | 0 |
M | 1.0011 |
E | 3 |
为什么9.5的二进制写法是1001.1而不是1001.101呢?
原因是二进制的权重
1 | 0 | 0 | 1 | . | 1 | 0 |
3 | 2 | 1 | 0 | -1 | -2 |
所以这也是为什么有些浮点数可能只有近似数却没有准确值的原因。
大概了解了S,M,E那么浮点数在内存中是怎么存储的呢?
存储浮点数在内存中的存储格式如下:
32位机器下:
S | 1bit | E | 8bit | M | 23bit |
64位机器下:
S | 1bit | E | 11bit | M | 52bit |
有效位M在存取时会省略1.xxxxxxxx前的1,只保存 xxxxxxxx部分,这样可以节省1bit,保存有效位24位。
指数位E情况比较复杂,首先指数E是无符号整数,就是说保存在E中的数据首位是数据位,但是实际的浮点数指数有正有负,所以必须选择另一种方式,表达正负的形式,那就是中间数。
中间数:就是取指数位数据范围的中间值,并在保存指数位的时候加上中间值,这样保证存入指数位的始终都是正数。
指数位数 | 指数范围 | 中间值 | |
32位机器下 | 8 | 0-255 | 127 |
64位机器下 | 11 | 0-2047 | 1023 |
举例:
当指数为5时,E=5+127=132,二进制写法1000 0100。
当指数为-1时,E=-1+127=126,二进制写法0111 1110。
读取读取分为三种情况:
一、E不全为0或者全为1是,E减去中间值,M前加上1,之后进行计算
二、E全为0时,E等于1-中间值,,M不再加上1,数字无限接近于0
三、E全为1时,如果有效数字M全为0,表示正负无穷大。
解释例题:
int main()
{
int i = 9;
float *pi = (float *)&i;
printf("1=%d\n", i);
printf("2=%f\n", i);
*pi = 9.0f;
printf("3=%d\n", i);
printf("4=%f\n", i);
system("pause");
return 0;
}
int i = 9;
内存中
0000 0000 0000 0000 0000 0000 1000 0001
使用int类型读取,结果为9
使用float类型读取,结果为0,因为E全为0.
*pi = 9.0f;
1001.0->1.001-> S = 0, M = 001,E = 1000 0010
内存中
0 10000010 00100000000000000000000
0100 0001 0001 0000 0000 0000 0000 0000
使用int类型读取,结果为1091567616
使用float类型读取,结果为9.000000.
注意:超出1个字节的数据类型都必须遵守大小端字节序。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)