目录
(1)当无符号数和有符号数进行比较时,会发生算数转换,比较两数的补码。
(2)当不同类型的数在同一个表达式中进行运算时,会发生算数转换。
一、 数据类型详细介绍
1.数据类型的基本归类
(1)整型家族
char 类型: char 、unsigned char、signed char
short类型: short/signed short、unsigned short
int类型: int/signed int、unsigned int
long(long long)类型: long/signed long、unsigned long
注:
1.短整型和长整型后的int可加可不加。
2.short类型、int类型、long类型在不写明unsigned或者signed的时候,系统默认为为有符号类型,但char类型由具体的编译器决定。
(2)浮点型家族
单精度浮点型float
双精度浮点型double
(3)构造类型
数组类型
结构体类型 struct
枚举类型 enum
联合类型 union
(4)指针类型
整型指针int* p
字符型指针char* p
浮点型指针float* p
空类型指针void* p
注:空类型指针可以接受所有类型变量或常量的地址,但是必须强制类型转换才能解引用,因为无法判断空类型指针解引用可以访问多大的空间。
2.整型在内存中的存储
整型在内存中以补码的方式存在。
(1)正整数
正数的原、反、补码都相同。
(2)负整数
负数的原、反、补码表示方式各不相同
原码:直接将数值转化为对应的二进制数,即为原码
反码:在原码的基础上,除符号位之外,其他位(数值位)按位取反
补码:反码+1
(3)补码存储数据的意义
符号位与数值位统一处理;加法减法统一处理
另外:原码 -> 补码 取反+1;补码 -> 原码 -1取反/取反+1 两种转换运算过程相同,不需要额 外的硬件电路。
二、原、反、补码的具体应用
1.截断和整型提升
例题:
#include <stdio.h>
int main()
{
unsigned char a = 200;
//00000000 00000000 00000000 11001000
//11001000 a 截取
unsigned char b = 100;
//00000000 00000000 00000000 01100100
//01100100 b 截取
//a+b 整型提升
//00000000 00000000 00000000 11001000 a
//00000000 00000000 00000000 01100100 b
//00000000 00000000 00000001 00101100 a+b 补码 正数,原码等于补码 300
unsigned char c = 0;
//00000000 00000000 00000000 00000000
//00000000 截断
c = a + b;
//00000000 00000000 00000001 00101100 截断得到 00101100 32+8+4=44 赋值给c
printf("%d %d", a + b, c);
return 0;
}
//输出结果: 300 44
当原数值的字节数大于将要赋值的对象时,会进行截断操作,选取从低位到高位截取相应长度的部分赋值;当数值输出前未及输出格式对应的字节长度,会进行整型提升,如果是无符号整型或有符号但符号位为0(为正),则整型提升的部分全为0,如果是有符号整型且符号位为1(为负),则整型提升的部分全为1。
注:在内存中的存储和表达式计算都是使用补码;打印是原码。
2.算数转换
(1)当无符号数和有符号数进行比较时,会发生算数转换,比较两数的补码。
举例:
#include <stdio.h>
int i;
int main()
{
i--;
if (i > sizeof(i))
{
printf(">\n");
}
else
{
printf("<\n");
}
return 0;
}
内容解释:i是全局变量,系统默认为初始化为0,i--;后i的值变为-1,此时i是有符号整型;而sizeof()是用来计算变量字节大小的,其返回值是无符号整型,当两数进行比较时,i进行算术转换转为无符号数,而-1的补码是11111111 11111111 11111111 11111111转为无符号数后表示2^32-1,很明显,比sizeof(i)也就是4大。所以输出结果是 > 。
(2)当不同类型的数在同一个表达式中进行运算时,会发生算数转换。
比如,当char类型、int类型、float类型、double类型的数进行运算时,会一步一步进行算数转换。
例题1:
假定 x 和 y 为 double 型,则执行 x=2; y=x+3/2; 后y的值为(D)
A 3.500000
B 3
C 2.000000
D 3.000000
思路分析:3/2==1,x和1进行运算前1从整型转换成double型与x进行加法运算,然后赋值给y,所以y的值为3.000000,故选D。
例题2:
char a; int b; float c; double d;
则表达式a*b+c-d值的类型为double类型。
三、大、小端字节序存储模式
1.大端字节序存储
(1)大端存储
数据的低位保存在内存的高地址中,数据的高位保存在内存的低地址中。
(2)小端存储
数据的低位保存在内存的低地址中,数据的高位保存在内存的高地址中。
2.意义
一个byte是8个bit位,当数据的存储需要大于一个字节的存储空间时,字节的排序成了一个问题,其他可能可行的排序方法都比较凌乱,读取比较困难,大小端的两种存储方式就比较方便。
例题:
请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。
#include <stdio.h>
int main()
{
int a = 1;
char* p = (char*)&a;
if(*p == 1)
printf("小端\n");
else
printf("大端\n");
return 0;
}
解释:char*类型指针p只能访问一个字节的空间,如果a是小端字节序,则p可以访问的部分是0x01,*p=1;如果是大端字节序,则p可以访问的部分是0x00,*p=0。可以由此判断是大端还是小端。
四、有符号位和无符号位字符类型的数值范围
-128~127(不能表示128)
五、浮点型在内存中的存储
1.举例
//浮点数在内存中的存储
#include <stdio.h>
int main()
{
int n = 9;
float* pFloat = (float*)&n;
printf("n的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
*pFloat = 9.0;
printf("num的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
return 0;
}
为什么输出结果两两不相同?
初步推测是因为整型和浮点型在内存中的存储方式不同。
2.浮点数存储规则
(1)详细解读
任意二进制数都可以表示为(-1)^S * 1.M * 2^e,其中S为0或1,M是尾数部分,e是指数部分。
例如:(-15)10 = (-1111)2 = (-1.111 * 2^3)2
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成标准化格式的浮点数: (-1)^S * M * 2^E,其中,E = e+127。
上面的例子中,S = 1,E = e + 127 = 1000 0010,M = 1110...0(最后一个1后面共20个0)
(-1)^S表示符号位,当S=0,V为正数;当S=1,V为负数。
M表示尾数部分。
E表示指数在内存中的实际存储。
(2)具体存储方式
float类型
32个比特位,最高位存放符号位;其次的8个比特位存放指数部分,实际存储的值为127+指数;剩余23个比特位存放有效值部分(尾数部分),因为必为1.。。。所以存储时将1舍弃,读取时再将1加上。
double类型
与上类似;
64个比特位,最高位存放符号位;其次的11个比特位存放指数部分,实际存储的值为1023+指数,如果出现全0的情况,则该数通常很小,无限接近于0;剩余52个比特位存放有效值部分(尾数部分),因为必为1.。。。所以存储时将1舍弃,读取时再将1加上。
结语:数据的存储是C语言的一个重要内容,希望大家都能理解并掌握。
本文中如果出现了错误,烦请各位大佬斧正!!!