1. 数据存储
1.1 数据类型
1.1.1 整形
char
unsigned char
signed char
short
unsigned short [int]
signed short [int]
int
unsigned int
signed int
long
unsigned long [int]
signed long [int]
1.1.2 浮点型
float
double
1.1.3 构造类型
// 数组
// 结构体 struct
// 枚举类型 enum
// 联合类型 union
1.1.4 指针类型
int* pi;
char* pc;
float* pf;
void* pv;
1.1.5 空类型
void // 空类型(无类型)
// 通常应用于函数的返回类型、函数的参数、指针类型
1.2 整形在内存中的存储
变量的创建需要在内存中开辟空间,空间的大小根据不同的类型而定。
int a = 100;
int b = -200;
// a分配四个字节的内存空间
// 无论是在32位还是64位环境下,int类型都是4byte
// 表示范围在limits.h中定义
1.2.1 原码、反码、补码
计算机中有符号数的三种表示方法。整数在计算机中以补码的形式存储。
- 原码
- 反码:原码符号位不变,其他位按位取反
- 补码:反码加一
正数的原码、反码、补码相同。
1.2.2 为什么数据在计算机中以补码形式存储?
在计算机系统中,数值一律用补码来表示和储存。因为使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理**(CPU只有加法器)**此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
1.3 大小端字节序
1.3.1 大小端介绍
- 大端(存储)模式:是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;
- 小端(存储)模式:是指数据的低位保存在内存的低地址中,而数据的高位,保存在内存的高地址中。

1.3.2 为什么又大端和小端?
在计算机系统中,以字节为单位,每个地址单元都对应着一个字节,一个字节为8bit。但是在C语言中除了8bit的char之外,还有16bit的short类型,32bit的int类型,以及long类型。另外,对于位数大于8位的处理器,例如16位或32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着如何将多个字节安排的问题。因此就导致了大端和小端两种存储模式。
1.3.3 相关习题
简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。
int main()
{
int a = 1;
char* p = (char*) &a;
if (*p==1)
{
printf("小端\n");
}
else {
printf("大端\n");
}
return 0;
}
1.4 浮点型在内存中的存储
1.4.1 浮点类型
float // 4byte
double // 8byte
long double // 8byte
// 表示范围再float.h中定义
1.4.2 IEEE754
C/C++中的浮点数采用IEEE754。根据过国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数V可以表示成下面的格式:
- (-1)S*M*2E (E为指数,2为基数,M为尾数)
- (-1)^S 符号位 当S=0,V为正数;当S=1,V为负数。
- M 有效数字,2>M≥1。
- 2^E 指数位。
// 浮点数举例
// 十进制:5.5
// 二进制:101.1 -> 1.011 * 2 ^ 2 1.011为尾数,2(第一个)为基数,2(第二个)为指数
// -> (-1) ^ 0 * 1.011 * 2 ^ 2
// -> S=0; E=2; M=1.011
其他规定
-
对于E,E是一个无符号整数。而科学计数法中E可能出现负值。为避免这种情况,在存储E时,对于float类型,再加上127;对于double类型,再加上1023。
// 例: // 十进制:0.5 // 二进制:0.1 (float) // 科学计数法:(-1)^0 * 1.0 * 2^(-1) // S=0; E=-1+127=126
-
对于M,M表示为1.xxxxxx,可见其中的1时固定不变的。所以在存储中可将其省略,将E变成xxxxxx,只将小数部分存储在内存中,节省1bit空间。
// 例: // 同上,十进制0.5的二进制表示为0.1(float) // 科学计数法:(-1)^0 * 1.0 * 2^(-1) // E=>1.000000=>00000

1.4.3 读取
-
E不全为1,也不全为0
指数E的计算值减去127(或1023),得真实值,再将M加上第一位的1。
-
E全为0
浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。为了表示+/-0,以及接近于0的很小的数字。
-
E全为1
如果有效数字M全为0,表示+/-无穷大(正负取决于符号位S)。
-
其他:浮点数在内存中也存在大小端问题。
1.4.4 实例1
int main()
{
int n = 9;
float* pf = (float*)&n; // 00000000000000000000000000001001
printf("n的值为:%d\n", n); // 9
printf("*pf的值为:%f\n", *pf); // 0.0
// 0 00000000 00000000000000000001001
// S=0; E=1-127=-126; M=0.00000000000000000001001
// 0.00000000000000000001001 * 2 ^ (-126)
// float的精度为6 则打印0.000000
*pf = 9.0; // 0 01111100 00100000000000000000000
printf("n的值为:%d\n", n); // 1091567616
printf("*pf的值为:%f\n", *pf); // 9.0
return 0;
}

1.4.5 实例2
int main()
{
float f = 9.0; // 0 01111100 00100000000000000000000
printf("%d\n", f); // 以浮点型存储,浮点型读取,整形打印
int* p = (int*)&f;
printf("%d\n", *p); // 以浮点型存储,整形读取,整形打印
return 0;
}

1.5 习题
1.5.1 一
int main()
{
char a = -1;
signed char b = -1;
unsigned char c = -1;
// -1的二进制
// 10000000000000000000000000000001 -> 原码
// 11111111111111111111111111111110 -> 反码
// 11111111111111111111111111111111 -> 补码
// 存放到char类型变量中,高位截断
// 在a b c中存放的都是11111111
printf("%d %d %d\n", a, b, c);
// %d打印整形,补码整形提升,打印原码
// a b 整型提升 高位补1
// c 无符号整型提升 高位补0
// a b 中的值为 11111111111111111111111111111111
// c 中的值为 00000000000000000000000011111111
// 以原码打印 a, b = -1; c = 255;
return 0;
}
// 补充
// 1. char到底是signed char 还是 unsigned char?
// C语言标准并没有规定,取决于编译器(但大多数编译器都是signed char)
// 2. int是signed int 还是 unsigned int?
// C语言规定int没有unsigned作为前缀时为signed int
2.5.2 二
int main()
{
char a = -128;
// 10000000000000000000000010000000
// a = 10000000
printf("%u\n", a); // %u打印无符号整数
// a是有符号char 整型提升 高位补1
// 11111111111111111111111110000000
return 0;
}
2.5.3 三
int main()
{
char a = 128;
// 00000000000000000000000010000000
// a 10000000
printf("%u\n", a);
// a是有符号char 整型提升 高位补1
// 11111111111111111111111110000000
}
// 10000000 在内存中直接被解析为-128
// 有符号char的取值范围为-128~127,如果存放的值大于127,则被解析为负数
// 例如128会被解析为-128
2.5.4 四
int main()
{
int i = -20;
// 10000000000000000000000000010100 -20 原码
// 11111111111111111111111111101100 -20 补码
unsigned int j = 10;
// 00000000000000000000000000001010 10
printf("%d\n", i + j);
// 相加
// 11111111111111111111111111110110 补码
// 10000000000000000000000000001010 原码 -10
// %d,认为内存中放的是有符号int类型
return 0;
}
// 站在内存的视角
2.5.5 五
int main()
{
int a = -20;
unsigned int b = 10;
if (a + b > 0) {
printf("a+b>0");
}else{
printf("a+b<=0");
}
return 0;
}
// int类型和无符号int类型相加,int类型转换为unsigned int类型

2.5.6 六
int main()
{
unsigned int i; // 无符号整数,i>=0恒成立
for ( i = 9; i >= 0; i--)
{
printf("%u\n", i);
}
return 0;
}
// 无限循环

2.5.7 七
#include <string.h>
int main()
{
char a[1000];
int i;
for ( i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d\n", strlen(a));
return 0;
}
// -1 - i
// -1 -2 -3 ... -128 127 126 ... 3 2 1 0('\0')
// 128 + 127 = 255

2.5.8 八
unsigned char i = 0;
int main()
{
for ( i = 0; i <= 255 ; i++)
{
printf("Hello World!\n");
}
return 0;
}
- 1. 数据存储
- 1.1 数据类型
* - 1.2 整形在内存中的存储
* - 1.3 大小端字节序
* - 1.4 浮点型在内存中的存储
* - 1.5 习题
*
- 1.1 数据类型