2. C 语言的基本数据类型
2.1 进位计数制
一个 \(r\) 进制数中,第 \(k\) 位的位权是 \(r^k\)。二进制转八进制时每 3 位一组,转十六进制时每 4 位一组,这比先转十进制省事。
- 十进制整数转 \(r\) 进制:除 \(r\) 取余,余数逆序排列;
- 十进制小数转 \(r\) 进制:乘 \(r\) 取整,整数部分顺序排列;
- \(r\) 进制转十进制:各位数字乘相应位权后求和。
有些十进制小数换成二进制会变成无限小数,所以浮点计算会带一点舍入误差。判断两个浮点数“相等”时,一般看误差是否足够小,不要想当然地直接用 ==。
2.2 整数在计算机中的表示
这几个概念容易混,按转换顺序记:原码 \(\rightarrow\) 反码 \(\rightarrow\) 补码。负数原码的最高位是 1;求反码时符号位不动,其余位取反;反码再加 1 就是补码。正数的三种表示相同。
机器里实际用补码存有符号整数。这样加减法可以共用一套运算规则,0 也只有一种表示。
偏移码可以由补码把符号位取反得到,正数最高位为 1,负数最高位为 0。浮点数的阶码就是用偏移码保存的。
定点数的小数点位置固定;浮点数则把数写成类似科学计数法的形式。课件按 IEEE 754 讲 float 和 double:一段存符号,一段存带偏移的阶码,剩下的存尾数。因此浮点数能表示很大的范围,但有效数字有限。
对 \(n\) 位整数:
- 无符号数范围为 \(0\sim 2^n-1\);
- 二进制补码有符号数范围为 \(-2^{n-1}\sim 2^{n-1}-1\)。
int 不一定永远是 32 位,具体大小看平台,可以用 sizeof 或 <limits.h> 查。无符号整数溢出后按模 \(2^n\) 回绕;有符号整数溢出不能这样推结果。
2.3 基本类型、常量与变量
基本类型先按下面这几类记:
- 整型:
char、short、int、long、long long,以及对应的unsigned类型; - 浮点型:
float、double、long double; - 空类型:
void。
常量的常见写法:
42 /* 十进制整数 */
052 /* 八进制整数,等于十进制 42;08 不是合法八进制常量 */
0x2A /* 十六进制整数 */
42U /* unsigned int */
42L /* long */
3.14 /* double */
3.14f /* float */
2.5e-3 /* 科学计数法 */
'A' /* 字符常量 */
"A" /* 字符串,占两个 char:'A' 和 '\0' */
转义字符包括 \n(换行)、\t(制表)、\\(反斜杠)、\'、\" 和 \0。字符在计算中按其字符编码值参与整型运算,如 '7' - '0' 得到整数 7。
变量先定义再使用。全局变量和静态变量没有手动给初值时会被置 0;普通局部变量不会,刚定义就拿来算通常会得到乱值。
const 表示不通过这个名字修改对象。它和宏常量不是一回事:const 变量有明确类型,也受作用域限制。
2.4 sizeof 与类型转换
sizeof 算一个类型或对象占多少字节,写成 sizeof(类型) 或 sizeof 表达式。有个容易出现在读程序题里的细节:sizeof(i++) 只看 i 的类型,并不会真的执行 i++。
不同类型混在一个表达式中时,会先按类型等级自动转换;赋值时还要再转成左边变量的类型。最常考的是整数除法:
double x = 5 / 2; /* 先做整数除法,x 为 2.0 */
double y = 5.0 / 2; /* y 为 2.5 */
int n = (int)3.8; /* 强制转换,截去小数部分,n 为 3 */
强制类型转换只管这一次计算,原变量的类型并没有变。