1.数据类型介绍:
char //字符数据类型
short //短整型
int //整形
long //长整型
long long //更长的整形
float //单精度浮点数
double //双精度浮点数
1.1类型的基本归类:
char char虽然是字符类型,但是字符类型存储的时候,存储的是字符的ASCII码值,ASCII值是整数
unsigned char
signed char
short
unsigned short [int]
signed short [int]
int
unsigned int
signed int
long
unsigned long [int]
signed long [int]
为什么要区分有符号或者无符号?因为生活中我们在描述的时候例如一个人的身高,年龄肯定就是无符号的了,温度就有正有负为无符号的。
short—2byte-16bit
0000 0000 0000 0000
0000 0000 0000 0001
0000 0000 0000 0010
..................................
1111 1111 1111 1111
如果是有符号的数据,最高位是符号位,最高位是0,表示正数,最高位是1,表示负数
如果对于无符号数来说,最高位也是数据位不是符号位
int short,在创建的时候如果没标明是unsigned 类型就默认signed类型,但是char类型没说我们就不确定有符号无符号,这个取决于编译器。
浮点数家族:
float
double
构造类型:(也可以叫自定义类型)
> 数组类型(int arr[10];类型是:int [10])
> 结构体类型 struct
> 枚举类型 enum
> 联合类型 union
指针类型
int *pi;
char *pc;
float* pf;
void* pv;
空类型:
void 表示空类型(无类型)
通常应用于函数的返回类型、函数的参数、指针类型
2. 整形在内存中的存储
我们之前讲过一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。
int a = 20;
int b = -10
2.1 原码、反码、补码
计算机中的整数有三种表示方法,即原码、反码和补码。 三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位 负整数的三种表示方法各不相同
整数可以写出三种2进制形式
原码
直接将二进制按照正负数的形式翻译成二进制就可以
反码
将原码的符号位不变,其他位依次按位取反就可以得到了。
补码
反码+1就得到补码。
正数的原、反、补码都相同。
对于整形来说:数据存放内存中其实存放的是补码
通过内存监控也可以看出是ff ff ff f6
那么为什么内存中存的是补码呢?
cpu中只有加法器,那怎么算减法呢?
1-1<=>1+(-1)
乘法跟除法无非也是多加几次或者多减几次
1:0000 0000 0000 0000 0000 0000 0000 0001
-1:1000 0000 0000 0000 0000 0000 0000 0001
sum:1000 0000 0000 0000 0000 0000 0000 0010
这里得到到结果是-2,但是1-1的结果应该是0,这里明显的计算错误了
我们看看补码的计算效果:
1:正整数原反补都一样
0000 0000 0000 0000 0000 0000 0000 0001
-1:
1111 1111 1111 1111 1111 1111 1111 1111
此时计算1-1
10000 0000 0000 0000 0000 0000 0000 0000
这时1是超出范围的,得出结果的时候这个值就丢了于是得到了
0000 0000 0000 0000 0000 0000 0000 0000
此时我们发现结果正确了
拓展:
signed char一个字符是1个byte->8个bit
0000 0000 解析: 0
0000 0001 1
0000 0010 2
.................
0111 1111 127
..................
1000 0000 解析的时候由于这个负数不能直接减一,所以直接解析位-128
1000 0001 先-1,再取反:1000 0000-1111 1111 :-127
.................
1111 1111 解析:1111 1110-1000 0001:-1
所以一个有符号的char存的值的范围只可能是:-128~~127
如果是unsigned char范围就是:0~255
如果unsigned和signed混用会有什么后果
%u:打印无符号数,意思是要打印的一定是无符号数,不是无符号数,我也认为是无符号数
-10:
原码:1000 0000 0000 0000 0000 0000 0000 1010
反码:1111 1111 1111 1111 1111 1111 1111 0101
补码:1111 1111 1111 1111 1111 1111 1111 0110
如果是unsigned int我们就认为他没符号位,
所以我们认为他的原码就是1111 1111 1111 1111 1111 1111 1111 0110使用计算器
答案正好是这个结果
%d:是打印有符号数,意思是要打印的一定是有符号数,不是有符号数,我也认为是有符号数
现在我们在谈一下数据储存为什么是倒着存储?
这里就涉及一个问题叫做大小端,
2.2 大小端介绍;(全称叫做大小端字节序存储)
什么大端小端:
比如说一个整型 16进制:0x 11 22 33 44(4个字节)一个16进制位可以转化为四个二进制位,两个16进制位可以转化为8个二进制位。
乱序的储存逻辑太复杂了,我们经过思考只留下了这两种存储排序。
第一种存储方式我们称为:大端字节存储:
第二种存储方式我们称为:小端字节存储:
是按照字节为单元来存储。
大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址 中;
小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地 址中。
为什么有大端和小端:
为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元 都对应着一个字节,一个字节为8bit。但是在C语言中除了8 bit的char之外,还有16 bit的short型,32 bit的long型(要看具体的编 译器),另外,对于位数大于8位 的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如 何将多个字节安排的问题。因此就 导致了大端存储模式和小端存储模式。 例如:一个 16bit 的 short 型 x ,在内存中的地址为 0x0010 , x 的值为 0x1122 ,那么 0x11 为 高字节, 0x22 为低字节。对于大端 模式,就将 0x11 放在低地址中,即 0x0010 中, 0x22 放在高地址中,即 0x0011 中。小端模式, 刚好相反。我们常用的 X86 结构是 小端模式,而 KEIL C51 则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以 由硬件来选择是大端模式还是小端模式。
例题:请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序
int main()
{
int a = 1;
//补码;0000 0000 0000 0000 0000 0000 0000 0000 0001
//16进制: 0 0 0 0 0 0 0 0 1
//=>0x 00 00 00 01
char*p=(&a);
if (*p == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
//输出什么?
int main()
{
char a = -1;
//-1的原码:1000 0000 0000 0000 0000 0000 0000 0001
//-1的反码:1111 1111 1111 1111 1111 1111 1111 1110
//-1的补码:1111 1111 1111 1111 1111 1111 1111 1111
//这里是char(1个字节=8个bit位)
//1111 1111 打印不够一个整型怎么办?
signed char b = -1;
unsigned char c = -1;
printf("a=%d,b=%d,c=%d", a, b, c);
//当我们打印a的时候,发生整型提升
//整型提升补的原来的符号位
//补完之后:1111 1111 1111 1111 1111 1111 1111 1111
//打印是有符号数,这是补码,打印是打印原码,得到原码
//1000 0000 0000 0000 0000 0000 0000 0001(-1)
//char b跟a思路相同
//c:
//-1的原码:1000 0000 0000 0000 0000 0000 0000 0001
//-1的反码:1111 1111 1111 1111 1111 1111 1111 1110
//-1的补码:1111 1111 1111 1111 1111 1111 1111 1111
//char c:1111 1111,整型提升,补满之后
//0000 0000 0000 0000 0000 0000 1111 1111(对于无符号数直接补0)
//这里按照%d打印,这个是正数,补码就是原码
//输出结果为255
return 0;
}