目录
C语言类型与数据的存储
C语言类型:
内置类型:char,short,int等等
自定义类型(构造类型)
类型的意义:决定了开辟内存空间的大小(大小决定了使用范围), 决定了看待内存空间的视角(相同的数字,整形和浮点型通过内存模块看到的结果是不一样的)
整型家族:char,short,int,long为什么char也算作整型?因为字符在电脑在电脑中存储为ASCII码值,整数
浮点型家族:float,double
构造类型:数组类型,结构体类型,枚举类型,联合类型
指针类型
空类型:void
数据的存储:
大端(存储)模式:是指数据的低位(如个位)保存在内存的高地址中,而数据的高位,保存在内存的低地址中
小端(存储)模式:是指数据的低位保存在内存的低地址中,而数据的高位,保存在内存的高地址中
字节序
如何验证本机的字节序?我们已经知道了大端和小端的存储模式,所以我们可以将一个整型变量存进去,因为字符变量是只能存储一个字节的,所以我们再用字符变量就可以查看低地址的字节是什么,由此来判断字节序
int 字节序()
{
int a = 1;
return *(char*)&a;
// ↑强制转换成字符指针类型
//大端:00 00 00 01
//小端:01 00 00 00
}
int main()
{
int ret = 字节序();
if (ret == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
整型提升与截断
整型提升的概念
整型提升是C语言中的规定,即在进行表达式的计算时,数据类型需要先转化为int型才能够进行
整型提升的规则
分为两种,一种为无符号整型,一种为有符号整型,其中无符号整型在进行整型提升的时候前面自动补0,有符号整型在进行整型提升的时候前面自动补最高位
截断的概念
当数据的大小超过该类型所能存储的大小时,就会发生截断,只存储能够保存的一部分
示例讲解
int main()
{
unsigned char a = 200;
//11001000
unsigned char b = 100;
//01100100
unsigned char c = 0;
//a和b整形提升(有符号数按照最高位补,无符号数直接补0)
//000000000000000011001000
//000000000000000001100100
//000000000000000100101100
//相加和存到c中,但是c只能存8位,发生截断,所以存的是00101100
c = a + b;
//以%d形式打印,c前再次补0,但是a+b的计算结果是按照有符号数得到的,所以以%d输出不会变
printf("%d %d\n", a + b, c);
return 0;
}
运行结果:
300 44
int main()
{
char a = -1;
//10000000000000000000000000000001原码
//11111111111111111111111111111111补码
//11111111字符类型8个比特位
signed int b = -1;
//11111111111111111111111111111111
unsigned int c = -1;
//11111111111111111111111111111111
signed char d = -1;
//11111111
unsigned char e = -1;
//11111111
printf("%d %d %d %d %d\n", a, b, c, d, e);
//为什么c打印出来不是4294967295?因为使用的是%d而不是%u
//为什么e打印出来是255?涉及到了整形提升与截断,整形提升之后是补码,注意换为原码进行打印
return 0;
}
运行结果:
-1 -1 -1 -1 255
int main()
{
char a = -128;
//1000 0000 0000 0000 0000 0000 1000 0000
// 1000 0000
//1111 1111 1111 1111 1111 1111 1000 0000
//%u打印,原反补相同
printf("%u\n", a);
return 0;
}
运行结果:
4294967168
整型的“循环”
int main()
{
char a[1000];
int i = 0;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d\n", strlen(a));
//因为数组为字符数组,又因为里面的数据为有符号整型,所以可取的数值范围是-128-127,有符号整型的数字变化为0,1...127,-128,-127...-1,所以从数组a中的数据从-1到-128之后为127,最后到0,再是-128.但是strlen遇到0就结束,所以结果为255
return 0;
}
int main()
{
unsigned char a = 0;
for (a = 0; a <= 255; a++)
{
printf("hello world\n");
}//死循环,因为a变为255后就又变成0了
return 0;
}
浮点型在内存中的存储
int main()
{
int n = 9;
float* p = (float*)&n;
printf("n的值为%d\n", n);
printf("*p的值为%f\n", *p);
*p = 9.0;
printf("n的值为%d\n", n);
printf("*p的值为%f\n", *p);
return 0;
}
运行结果:
n的值为9
*p的值为0.000000
n的值为1091567616
*p的值为9.000000
为什么会出现上面这种情况呢?原因是在计算机中整型的存储和浮点型的存储是不一样的,所以会导致出现上面这种情况
浮点数在计算机内部的表示方法:
根据国际标准IEEE754,任意一个二进制浮点数v可以表示成以下形式
(-1) ^ s* m * 2 ^ e
(-1) ^ s表示符号位,s=0时,v为正数;s=1时,v为负数
m表示有效数字,大于等于1,小于2
2 ^ e表示指数位
IEEE754规定,对于32位的浮点数,最高的1位是符号位s,接着的8位是指数e,剩下的23位为有效数字m
由于m是大于等于1,小于2的,所以我们忽略1,直接将后面的23位全存储小数,拿出来之后再在加一个整数部分1,这样我们就可以保存24位有效数字了
IEEE754规定,对于64位的浮点数,最高的一位是符号位s,接着的11位是指数e,剩下的52位为有效数字m
e为一个无符号整数,如果e为8位,取值范围为0-255,如果是11位,取值范围为0-2047
由于科学计数法中的e是可以出现负数的,所以IEEE754规定,存入内存时e的真实值必须加上一个中间数,对于8位的e,这个中间数是127,对于11位的e,这个中间数是1023
比如:2^10的e
以9.0为例,9.0的二进制浮点数小数点前后分开表示,1001.0,
0.5的二进制浮点数表示为0.1,因为小数点后的1为1*2^-1,0.01的十进制为0.25
1001.0--->(-1)^0*1.001*2^3--->(-1)^0*1.001*2^(3+127)--->(-1)^0*1.001*2^(130)0.1--->(-1)^0*1*2^(-1+127)--->(-1)^0*1*2^(126)
5.5 101.1 (-1)^0*1.011*2^2
0 10000001 01100000000000000000000
↑129 ↑011为小数位,后面补齐至23位
e的取出
e不为全0或不为全1:
e转十进制,减去127(或1023),得到e的真实值,小数部分前加一个“1.”,
e为全0:
这时,指数e为1-127(或1-1023),有效数字m不再加上第一位的1,而是还原为0.xxxx,这样做是为了表示±0,以及接近于0的很小的数字
e为全1:
这时,表示无穷大,正负数取决于符号位
int main()
{
int n = 9;
/*00000000000000000000000000001001原码(补码)*/
float* p = (float*)&n;
printf("n的值为%d\n", n);
printf("*p的值为%f\n", *p);/*以浮点数角度看,00000000000000000000000000001001是(-1)^0*0.00000000000000000001001*2^-126,近似为0*/
*p = 9.0;
/*(-1)^0*1.001*2^3--->0 10000010 00100000000000000000000,从整型角度来看这就是一个二进制反码,又因为最高位为0,所以正数的原反补相同,所以这就是原码,从而输出十进制数*/
printf("n的值为%d\n", n);
printf("*p的值为%f\n", *p);
return 0;
}