skip to content
Astro Cactus

C Programming 基本类型的摘要

/ 6 min read

Updated:

Basic Data Types and hardware representations

char hardware
  • 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 float hardware

    • 浮点数表示的公式:$(-1)^{s}\times m\times 2^{e}$
      • s 是sign,s = 0时浮点数是正数,s=1时浮点数是负数
      • m 是尾数
        • 尾数的定义:指有理数之中浮点数位于小·数点后面的数
      • e 是指数
        • 关于指数的定义:指数是幂运算aⁿ(a≠0)中的一个参数,a为底数,n为指数
    • 浮点数的精度(Precision) float precision
      • 因为0到1之间有无数个数,而尾数的bits是有限的,必然只能精确表示一小部分的数,其他的数会出现精度丢失。

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

  1. smaller signed integer → longer signed integer
    • 符号拓展,如果是正数,拓展0;如果是负数,拓展1
      • 1111111011111111 11111110
      • 0000001000000000 00000010
  2. smaller unsigned integer → longer unsigned integer
    • 零拓展,高位用0补充
  3. longer integer → shorter integer (有坑)
    • 高位截断,保留低位 00000000 00000001 11111111 11111111 (32)→ 11111111 11111111 (16)
    • 可能会导致数值变化 11111111 11111111 00000000 0000000100000000 00000001
  4. 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)的优先级,减轻心智负担。