| 整形家族 | 分支 | 备注 |
|---|---|---|
| char | unsigned char | C语言标准没有规定char是指signed char还是unsigned char,这取决于编译器 |
| signed char | ||
| short | unsigned short [int] | C语言标准规定short是指signed short |
| signed short [int] | ||
| int | unsigned int | C语言标准规定int是指signed int |
| signed int | ||
| long | unsigned long [int] | |
| signed long [int] |
| 浮点数家族 |
|---|
| float |
| double |
| 构造类型 |
|---|
| 数组类型 |
| 结构体类型struct |
| 枚举类型enum |
| 联合类型union |
| 指针类型 |
|---|
| int *pi; |
| char *pc; |
| float *pf; |
| void *pv; |
| 空类型 | 用途 |
|---|---|
| void | 1. 函数的返回类型 |
| 2. 函数参数 | |
| 3. 指针 |
#2 整数存储
定义在limits.h头文件中。
数据在内存中以2进制形式存储。
整数的二进制有3种表现形式:
- 原码
- 反码
- 补码
对于正整数来说,原码反码补码相同;
对于负整数来说,原码反码补码不同,需要计算才能得到:
原码:取绝对值后,将符号位的0变为1
反码:原码除符号位外,其他位取反
补码:反码+1
补码转化为原码和原码转化为补码的计算过程是相同的。
整数在内存中存储的是补码!
内存中的字节存储形式。
大端字节序:把数据的低位字节的内容存放在高地址处,高位字节的内容存放在低地址处。
小端字节序:把数据的低位字节的内容存放在低地址处,高位字节的内容存放在高地址处。
判断当前系统的字节序:
int judge(void)
{
int num = 1;
char* pa = (char*)# // 注意这里的强制类型转换
//if(*pa == 1)
// return 1; // 返回1,表示小端字节序
//else
// return 0; // 返回0,表示大端字节序
return *p; // 由上面可得,返回的数与*p相同,因此可以直接返回*p
}
#2.2 例题
下列代码的运行结果为(以系统为32位为例):
int main()
{
char a = -128;
printf("%u", a);
return 0;
}
a=-128,原码为1000 0000 0000 0000 0000 0000 1000 0000
反码为1111 1111 1111 1111 1111 1111 0111 1111
补码为1111 1111 1111 1111 1111 1111 1000 0000
因此a在内存中为1000 0000
因为a为signed char(一般编译器认为char为signed char),因此整型提升时,按符号位进行补位,结果为:
1111 1111 1111 1111 1111 1111 1000 0000
用%u打印时,最高位不看做符号位,而是数值,
因此为1111 1111 1111 1111 1111 1111 1000 0000 = 4294967168
如果用%d打印时,最高位为符号位,而且因为1111 1111 1111 1111 1111 1111 1000 0000为补码,要想知道其在有符号位时代表十进制的哪个数,需要先求出其原码,方法:除符号位外全部取反,再加1,最终得到
1000 0000 0000 0000 0000 0000 1000 0000=-128
下列代码的运行结果为(以系统为32位为例):
int main()
{
char a = 128;
printf("%un", a);
return 0;
}
128=0000 0000 0000 0000 0000 0000 1000 0000
正数的原码反码补码相同,因此a为1000 0000。
因为a为signed char(一般编译器认为char为signed char),因此整型提升时,按符号位进行补位,结果为:
1111 1111 1111 1111 1111 1111 1000 0000
用%u打印时,最高位不看做符号位,而是数值,
因此为1111 1111 1111 1111 1111 1111 1000 0000 = 4294967168
下列代码的运行结果为(以系统为32位为例):
int main()
{
char a[1000];
int i;
for(u = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d", strlen(a));
return 0;
}
上面的代码随着循环的进行,a数组依次存入-1、-2、…、-127、-128、127、…、1后,存入0,接着存入1、…。数字0对应ASKII码为‘ ’,为字符串的结尾处,因此最终结果为255。
定义在float.h头文件中。
一个例子:
int main()
{
int n = 9;
float* pFloat = (float*)&n;
printf("n的值为:%dn", n);
printf("*pFloat的值为:%fn", *pFloat);
*pFloat = 9.0;
printf("n的值为:%dn", n);
printf("*pFloat的值为:%fn", *pFloat);
// printf("*pFloat用%d打印为:%nn", *pFloat); // 这样写会报错
printf("%d", 9.0);
return 0;
}
结果为:
n的值为:9
*pFloat的值为:0.000000
n的值为:1091567616
*pFloat的值为:9.000000
0
任何一个二进制浮点数V可表示成下面形式:
- (-1)s * M * 2E
- (-1)s表示符号位,当s=0时,V为正数;当s=1时,V为负数
- M表示有效数字,大于等于1,小于2
- 2E 表示指数位
十进制浮点数5.5转化成二进制表示:101.1 —> 1.011 * 22 —>(-1)0 * 1.011 * 2E ;对应S=0,M=011,E=2
对于32位浮点数,最高位为符号位S,接着的8个位为指数E,剩下的23位为有效数字M
双精度浮点数的指数E有11位,有效数字M为52位。
E为无符号数,对于8位的E,取值范围为0 ~ 255;11位的E,取值范围为0 ~ 2047。但科学计数法中的E可以是负数,因此存入内存时E的真实值必须加上一个中间数。对于8位的E,中间数为127,11位的E,中间数为1023.例如,十进制的0.5,转化为二进制为0.1 —>1 * 2-1 ,此时的E为-1,在存入内存前,需要加上127,最终存入内存的数为126。
当E全为0时,有效数字M不再加上第一位的1,而是还原为0.xxxx。这样做是为了表示±0,以及接近0的很小的数。
当E全为1时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s)。
回到一开始的例题。9的二进制为0000 0000 0000 0000 0000 0000 0000 1001,按float的存储规则可得,0 00000000 00000000000000000001001,E=0-127=-127,是一个很小的数,约等于0。
9.0的二进制为1001.0—>1.001*23 ,按照float的存储规则可得,S=0,E=3+127=13=10000010,M=001(不足23位,在后面补0),存入存储中的二进制为0 10000010 00100000000000000000000,用%d打印时为1091567616。
注意:printf()函数打印时,会把float转换成double类型。
因此,printf(“%d”, 9.0);才会输出0,因为转成double后为64位,用%d输出,截取后面的32位,而后面的32个位都是0,所以才输出为0。



