Basic Data Types and hardware representations
-
char
- 8位,解释时用的是ASCII字符编码表(American Standard Code for Information Interchange)
- 写’A’而不是65是一种抽象的手段,使得我们可以不花心思记忆ASCII而是直接写对应的符号
-
signed and unsigned int
- unsigned int 用的是正码编码,所以最大能表示 2^{32}
- int 用的是二进制的补码,它可以同时表示正数和负数,最高位是1为正数,最高位是0为负数
- 计算机科学家之所以采用补码表示正负数,是因为两个补码表示的数可以直接相加得到另一个补码表示的数
- y和z的硬件表示是一样的,如果只是读取这一段序列你根本无法知道它是什么。这时候程序代码和编译器就决定了它究竟是什么类型,从而也就确定了它是什么值。
-
float and double

- 浮点数表示的公式:$(-1)^{s}\times m\times 2^{e}$
- s 是sign,s = 0时浮点数是正数,s=1时浮点数是负数
- m 是尾数
- 尾数的定义:指有理数之中浮点数位于小·数点后面的数
- e 是指数
- 关于指数的定义:指数是幂运算aⁿ(a≠0)中的一个参数,a为底数,n为指数
- 浮点数的精度(Precision)
- 因为0到1之间有无数个数,而尾数的bits是有限的,必然只能精确表示一小部分的数,其他的数会出现精度丢失。
- 浮点数表示的公式:$(-1)^{s}\times m\times 2^{e}$
Expression Types
表达式也有类型
- 常量是最简单的表达式
- 3.14, -0.819诸如此类的常量,它们的类型是double(而不是float)
- 想要修改常量的类型,可以添加后缀
- 3.14f是float而不是double
- 999L是long int而不是int
Type Conversion
假设a是int,b是float,a + b的类型是什么?很简单,答案是float。这个过程是由哪部分实现的?编译器。
- 编译器把两个操作数转换成相同的类型,具体转换的规则还挺复杂的,但是有一条经验公式:尽量保证运算的结果在数学上是精确的。譬如这个例子,float + int = float
- 编译器转换指令的原理是,在表达式执行前的代码插入类型转换的指令,可能还涉及修改具体调用的操作指令,例如整型操作指令(如ADD、SUB)改为浮点操作指令(如FADD、FMUL)
常见的4种 type conversion
- smaller signed integer → longer signed integer
- 符号拓展,如果是正数,拓展0;如果是负数,拓展1
11111110→11111111 1111111000000010→00000000 00000010
- 符号拓展,如果是正数,拓展0;如果是负数,拓展1
- smaller unsigned integer → longer unsigned integer
- 零拓展,高位用0补充
- longer integer → shorter integer (有坑)
- 高位截断,保留低位
00000000 00000001 11111111 11111111(32)→11111111 11111111(16) - 可能会导致数值变化
11111111 11111111 00000000 00000001→00000000 00000001
- 高位截断,保留低位
- int → float
举个例子:
unsigned int bigNum = 100;int littleNum = -100;if (bigNum > littleNum) { printf("Obviously, 100 is bigger than -100!\n");}else { printf("Something unexpected has happened!\n");}执行bigNum > littleNum时,littleNum(signed int)会转换为unsigned int,比较的结果是false!这种情况下得自己实现比较的方法
Casting
casting一般翻译为“显式转换”,type conversion一般翻译为“隐式转换”。
When a programmer does this, it is called casting and when a compiler does it, it is called type conversion or type promotion.
为什么会有casting的需求?
int main(void) { int nHrs = 40; int nDays = 7;
float avg = nHrs/nDays; printf("%d hours in %d days\n", nHrs, nDays); printf("work %.1f hours per day!\n", avg); // expect 7.14, result 7 //...}float avg = nHrs/nDays; 相除的两个数是整数,nHrs/nDays 是一个int,然后再经由float操作符转换为float类型,于是avg的值为5.0
使用casting来解决这个问题:
int main(void) { int nHrs = 40; int nDays = 7;
float avg = nHrs/(float)nDays; printf("%d hours in %d days\n", nHrs, nDays); printf("work %.1f hours per day!\n", avg); //...}先将一个值转为float,然后通过编译器的类型转换把另一个值也转为float,使得表达式的类型为float。
casting的写法,习惯上写作a / (double) b而不是(double) a/ b,虽然两者等价,但是前者不需要你去判断/和(double)的优先级,减轻心智负担。