环境:CLion2021.3;64位macOS Big Sur
数据的分类:
1.整型 2.浮点型 3.构造类型(自定义类型):数组、结构体、枚举、联合体 4.指针型
5.空类型void,可用于函数返回值void test()、函数参数void test(void)、指针void* p
七种基本类型:char、short、int、long、long long、float、double.
在32位编译器下经sizeof测试后的占用空间情况如下:
数据类型 | 占用空间大小 (byte) |
---|---|
char | 1 |
short | 2 |
int | 4 |
long | 4 |
long long | 8 |
float | 4 |
double | 8 |
在64位编译器下经sizeof测试后的占用空间情况如下:
数据类型 | 占用空间大小 (byte) |
---|---|
char | 1 |
short | 2 |
int | 4 |
long | 8 |
long long | 8 |
float | 4 |
double | 8 |
int:可定义成十进制、八进制或者十六进制,输出时用相应的格式控制符号就可以,可以与char相互转换。但要注意,若int为负数,不能转换,因为ASCLL 码没有负数。
int a = 0x32;//定义16进制以0x开头
int b = 017;//定义8进制以0开头
printf("%d\n" ,a + b);//%d代表输出10进制,%u代表输出无符号10进制
printf("%x\n", a + b);//%x代表输出16进制
printf("%o\n", a + b);//%o代表输出8进制
printf("%c\n", a + b);//%c代表输出字符,根据ACSLL码找到相应的字符
float和double:若只给出一个浮点数(指常量而非定义的变量),那么这个浮点数默认为double类型,可以在后边加f来变成float类型。
3.0;//double类型
5.0f;//float类型
float a = 1;//float类型 (通过sizeof判断,结果为4)
1.整型在内存中以补码形式存放,注意char也是整型:
char:unsigned/signed short:unsigned/signed int:unsigned/signed long:unsigned/signed
short和int默认为signed型,而char默认为有符号还是无符号C语言没有规定,取决于编译器,不过大部分编译器默认是signed。
用补码存储的原因:
CPU只有加法器而没有减法器,使用补码可以将符号位和数值域统一处理;同时加法和减法也可以统一处理;此外,补码与原码相互转换的运算过程是相同的,不需要额外的硬件电路。
正整数的原码=反码=补码;
负整数反码为其原码除符号以外按位取反,补码为反码加1。因此从补码得到原码一般思路是先减1,再除符号位外按位取反,当然是可行的。其实将补码除符号位按位取反再加1,也可以得到原码,这样做的好处是统一了原码与补码的转换过程。
2.大端存储与小端存储
大端字节序和小端字节序:以字节为单位考虑数据地存储顺序。
大端字节序:把数据的高位字节序的内容存放在低地址处,低位字节序的内容存放在高地址处。
小端字节序:把数据的低位字节序的内容存放在低地址处,高位字节序的内容存放在高地址处。
判断当前机器采用大端存储还是小端存储:
int byteSqu()
{
int a = 1;
char* p = (char*)&a;
return *p;//大端返回0,小端返回1
}
3.练习:
(1)
char a = -1;
signed char b = -1;
unsigned char c = -1;
//10000000 00000000 00000000 00000001 -> 11111111 11111111 11111111 11111110 -> 11111111 11111111 11111111 11111111
//-> 11111111 - a -(整型提升) 11111111 11111111 11111111 11111111 = -1
//11111111 - b -(整型提升) 11111111 11111111 11111111 11111111 = -1
//11111111 - c -(整型提升) 00000000 00000000 00000000 11111111 = 2^8 - 1 = 255
printf("%d %d %d", a, b, c);
有关整型提升,见地表最强C语言汇总(八)操作符8.11
(2)
char a = -128;
//10000000 00000000 00000000 10000000
//11111111 11111111 11111111 01111111
//11111111 11111111 11111111 10000000
//10000000 - a(整型提升,注意是按照原变量的类型确定符号) - 11111111 11111111 11111111 10000000 - 在%u的视角下,此数为正数,其补码就是原码
printf("%u\n", a);//4294967168
(3)
char a = 128;
printf("%u\n", a);//4294967168
char类型的取值范围:
有符号:-128 – 127(10000000被解析成-128)且127 + 1 = -128
无符号:0 – 255
(4)
int i = -20;
unsigned int j = 10;
//10000000 00000000 00000000 00010100 -> 11111111 11111111 11111111 11101100
//00000000 00000000 00000000 00001010
//11111111 11111111 11111111 11101100
//11111111 11111111 11111111 11110110 -> 100000000 00000000 00000000 00001010 = -10
printf("%d\n", i + j);
(5)
unsigned int i = 0;
for (i = 9; i >= 0; i--)//死循环
printf("%u\n", i);//打印是正负再打印的时候不取决于i本身,而是取决于打印的格式
(6)
char a[1000];
int i = 0;
for (i = 0; i < 1000; i++)
a[i] = -1 - i;
//-1 -2 -3 ... -128 127 126 ... 1 0 -1 -2 -3 ...
printf("%d", strlen(a));//255
//char:超出范围的数据若为正数,-256;若为负数,+256
(7)
unsigned char i = 0;
for (i = 0; i <= 255; i++)//死循环,无符号char最大值为255
printf("hello world\n");
IEEE754标准规定,任意一个二进制浮点数V可以表示为下面的形式:
1.(-1)^S * M * 2^E;
2.(-1)^S表示符号位,S=0时V为正数;S=1时V为负数;
3.M表示有效数字,大于等于1,小于2;
4.2^E表示指数位;
其中:
1.对于32为浮点数,即float,最高一位为符号位,接着是8位指数位E,最后是23位有效数字位M,
对于64位浮点数,即double,最高一位为符号位,接着是11位指数位E,最后是52位有效数字位M;
2.M:由于1<=M<2,因此M小数点前必为1,没有必要存储,所以只存小数点后边的数据即可,这样M就可以存储24(53)位有效位。
3.E:E为无符号整数,但是指数可正可负,因此E存入内存的值为E的真实值加上一个中间数,若E为8位,这个中间数为127;若E为11位,这个中间数为1023。
无符号8位可以表示的范围:0 – 255,减去127后:-127 – 128,这就是float实际能表示的指数的范围
无符号11位可以表示的范围:0 – 2047,减去1023后:-1023 – 1024,这就是double实际能表示的指数的范围。
4.数据根据指数E从内存中取出的三种情况:
(1)E不全为0且不全为1:
E(真实) = E -127(1023),再将有效数字M加上第一位的1;
(2)E全为0:
此时真实的E很小,规定E(真实) = 1 - 127(1023),有效数字M不需加上第一位的1,而是还原为0.xxxxxx,用来表示±0或很接近0的数;
(3)E全为1:
根据符号位来表示±无穷大
练习:
float f = 5.5f;
//101.1 --> 1.011 * 2^2
//S = 0 E = 2 M = 1.011 ---真实值
//S = 0 E = 129 M = 011 ---实际存储
//0 10000001 01100000000000000000000
//01000000 10110000 00000000 00000000
//40 b0 00 00
int n = 9;
//00000000 00000000 00000000 00001001
float* pFloat = (float*)&n;
printf("n的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);//以浮点数的视角打印结果
// S E M
//存: 0 00000000 00000000000000000001001
//取: 0 1-127=-126 0.00000000000000000001001
//0.00000000000000000001001 * 2^-126 --> 0.000000
*pFloat = 9.0;//以浮点数的视角存储9.0
//1001.0 --> 1.001 *2^3
// S E M
//真实 0 00000011 00100000000000000000000
//实际存储 0 10000010 00100000000000000000000
//int取 01000001 00010000 00000000 00000000 -- 1,091,567,616
printf("n的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);