本文是C语言进阶系列的第一期,难度不是很大,但是对于整个学习的过程非常有帮助。
char //字符数据类型
short //短整型
int //整型
long //长整型
long long //更长的整型
float //单精度浮点数
double //双精度浮点数
这些类型都是比较常见的数据类型,这里再介绍一种类型:布尔类型
在C99标准后,用0表示假,用非0表示真,布尔类型就是如此:
#include
int main() { _Bool flag = false; if(flag) { printf("hehe"\n"); } return 0; } 在这段代码中并不会打印hehe,因为布尔类型定义了flag是假的,所以flag代表的就是0
(其实就是int类型的一种,真就是int定义为1,假就是int定义为0)
char:
unsigned char
signed char
short:
unsigned short[int]
signed short[int]
int:
unsigned int
signed int
long:
unsigned long[int]
signed long[int]
在C语言中,unsigned的类型是没有负数的,即unsigned>=0,如果存放的是负数那么会转换成一个整数
int 等价于 signed int,short 等价于 signed short, long 等价于 signed long
但是!char不一样!并没有规定char等价于什么,但是在常见的编译器中 char 等价于unsigned char
也叫 自定义类型
数组类型 结构体类型 struct 枚举类型 enum 联合类型 union
2.1 介绍
整数的二进制表示有三种表示形式:原码、反码、补码
正数:原码反码补码相同
负数:
原码:按照一个数的正、负直接写出来的二进制就是原码
反码:符号位不变,其他位按位取反
补码:反码的二进制序列+1
如:
int a = -10
10000000 00000000 00000000 00001010 原码
11111111 11111111 11111111 11110101 反码
11111111 11111111 11111111 11110110 补码
#include
int main() { char a = -1; signed char b = -1; unsigned char c = -1; printf("%d %d %d",a,b,c); return 0; } 这段代码中的a,b,c分别打印出来是多少呢?
-1在内存中的存放的是32个比特位,4个字节,也就是
11111111 11111111 11111111 11111111 --补码
但是char类型的只能存1个字节,也就是后面的11111111
于是发生整形提升,按照第一个数字(符号位)提升
于是补码变成了
11111111 11111111 11111111 11111111 所以说打印出来的仍然是-1
但是unsigned char不一样了!
11111111整形提升后,高位因为无符号的原因,补得是0
于是补码变成了00000000 00000000 00000000 11111111
由于这是一个正数,所以原码是一样的,打印出来正好就是255
#include
int main() { char a = -128; printf("%u",a); return 0; } %u是打印一个无符号的数,-128在内存中的原码是
10000000 00000000 00000000 10000000
补码是
11111111 11111111 11111111 10000000
整形提升后
11111111 11111111 11111111 10000000
但是%u会把这当成无符号数,所以会把这个当成正数,原码反码补码相同
于是这个数打印出来就是4,294,967,168
#include
int main() { char a[1000]; int i = 0; for(i = 0;i<1000;i++) { a[i] = -1-i; } printf("%d",strlen(a)); return 0; } 这里的a是多少呢?是不是1001呢?我们仔细来分析:
先说结论:
signed char 取值范围:-128 ~ 127
unsigned char 取值范围:0 ~ 255
我们先看unsigned char:
这是char类型的所有可以存放的数据,由于是无符号类型的char,所以第一位并不是符号位,8位数字都存放的是有效位,所以可以存放0-255的数据
我们再来分析signed char:
由于第一位是符号位,所以存放的数据只有128。并且特别说明,10000000会被直接识别出来为-128
也就是说,有符号的char是127时,再加1就变成了-128
为什么计算机中存储的是补码呢?首先我们要知道计算机中只有加法器,减法也得当成加法来算:1+(-1)
因为只有补码能满足各种数据的运算~
可以看到这里的数据超过了一个字节的大小,在内存中存放的时候是有顺序的。
大端字节序存储:11223344按照从低到高的顺序存储。1在低地址,4在高地址。
准确来说是:当一个数据的低字节数据存放在高地址处,高字节的内容放在了低地址处,这种储存方式就是大端字节序存储。
那么VS2019中就是小端字节序存储。
问题来了:写个程序判断当前编译器是大端字节序还是小端字节序
int main() { int a = 1; char* p = (char*)&a; if(1 == *p) { printf("小端"); } else { printf("大端"); } return 0; }
原理是什么呢?
#include
int main()
{
int n = 9;
float* pfloat = (float*)&n;
printf("%d\n",n);
printf("%f\n",*pfloat);
*pfloat = 9.0;
printf("%d\n",n);
printf("%f\n",*pfloat);
return 0;
}
但是这个时候我们发现并不是我们想要的结果。
整数和浮点数在内存中的存储是不一样的。
那么浮点数在内存中是如何存储的呢?
根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数V可以表示成下面的形式:
首先以5.5为例,5.5的二进制是101.1
那么用754标准,得出为:
-1^0 * 1.011 * 2^2
S = 0;M = 1.011;E = 2
我哦们可以发现这个M总是大于1小于2的
关于M:
IEEE754规定,在计算机内部保存M时,默认这个数的第一位是1,因此可以被舍去,只保存后面的部分,比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去,这样做的目的,是节省1位有效数字,以float为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字
关于E:
首先,E为一个无符号整数。这意味着,如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0~2047.但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,存入内存时E的真实值必须再加上一个中间数。对于8位的E,这个中间数是127,对于11位的E,这个中间数是1023.比如,2^10的E是10,所以保存成10+127=137,即10001001
然后,指数E从内存中取出还可以分成三种情况:
1.E不全为0或不全为1
这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。
比如:
0.5 (1/2)的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位,
则为
1.0*2^(-1),其阶码为-1+127=126,表示为
01111110,而尾数1.0去掉整数部分为0,补齐0到23位00000000000000000000000,则其二进制表示形式为:0 01111110 000000000000000000000002.E为全0
这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,
有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示+0,以及接近于0的很小的数字。3.E全为1
这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s);
#include
int main()
{
int n = 9;
float* pfloat = (float*)&n;
printf("%d\n",n);
printf("%f\n",*pfloat);
*pfloat = 9.0;
printf("%d\n",n);
printf("%f\n",*pfloat);
return 0;
}
最后我们再来看到这个代码段:
第一个n = 9,那么9存放的是00000000000000000000000000001001
但是对于浮点数9,第一个0代表的是S,中间8位代表的是E,最后23位表示的是M。
E为全0,这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,这个值是非常小的,有效位在小数点后面很多位,所以打印6位浮点数出来为全0;
第二个*pfloat = 9.0,9.0是十进制的表示,转换为相应的科学计数法为:
0 10000010 00100000000000000000000
这个数浮点数识别出来的就是9.0
我们可以清楚地看到整数和浮点数在内存中的存储是不同的!
也许面试的时候并不会考这么深的知识,但是这是非常修炼内功的知识!
那么今天的知识分享就到这里了,觉得写的还不错的话点赞收藏关注博主吧!