晋江文学城
下一章 上一章  目录  设置

3、第002章_编译错误 ...

  •   # Chapter 002: Compilation Error
      # Status: Timeline 1972.11.3
      # Location: Bell Labs, Building 2

      >>> Timeline Jump Complete
      >>> Current Location: Bell Labs, 1972
      >>> Mission: Fix critical compilation error in C compiler
      >>> Warning: This bug could affect all future C programs
      >>> Status: Initializing local environment...

      世界重新聚焦时,我发现自己坐在一张堆满纸张的办公桌前。桌上放着一台更新的终端机,型号是PDP-11/45。

      "嘿,你能帮我看看这个编译器的问题吗?"

      我转过头,看到Dennis Ritchie正在翻看一叠打印纸。这次我来到了C语言诞生的关键时刻。

      $ ls
      cc.c
      cgen.c
      cgram.y
      clex.c
      sym.h
      ...

      这是最早期的C编译器源代码。据历史记载,在C语言发布前,有一个严重的编译错误差点导致整个项目延期。

      我调出了编译器的源代码:

      typedef struct {
      int t_op;
      int t_type;
      int t_value;
      struct node *t_left;
      struct node *t_right;
      } node;

      void compile(node *tree) {
      if (tree == NULL) return;

      switch(tree->t_op) {
      case PLUS:
      compile(tree->t_left);
      compile(tree->t_right);
      emit("ADD");
      break;
      case ASSIGN:
      if (tree->t_left->t_type != tree->t_right->t_type) {
      // 类型检查有问题
      implicit_cast(tree->t_right, tree->t_left->t_type);
      }
      compile(tree->t_right);
      store(tree->t_left);
      break;
      // ...
      }
      }

      "看到问题了吗?"Ritchie问道,"有些程序编译时会产生错误的代码,特别是涉及类型转换的时候。"

      我仔细检查着代码。这是早期C语言最关键的部分之一:类型系统。如果这里出现问题,将影响到未来几十年的程序开发。

      突然,我注意到了问题所在:

      void implicit_cast(node *expr, int target_type) {
      if (expr->t_type == INT && target_type == FLOAT) {
      emit("ITOF");
      } else if (expr->t_type == FLOAT && target_type == INT) {
      emit("FTOI");
      } else if (expr->t_type == CHAR && target_type == INT) {
      // 这里漏掉了符号扩展
      emit("LOAD1"); // 只加载一个字节
      }
      expr->t_type = target_type;
      }

      "找到了,"我说,"类型转换的实现有问题。"

      这个bug如果不修复,将导致所有从char到int的转换都忽略符号位,使得负数变成大的正数。这在后来会造成无数的程序错误。

      我开始修改代码:

      void implicit_cast(node *expr, int target_type) {
      if (expr->t_type == INT && target_type == FLOAT) {
      emit("ITOF");
      } else if (expr->t_type == FLOAT && target_type == INT) {
      emit("FTOI");
      } else if (expr->t_type == CHAR && target_type == INT) {
      emit("LOADS"); // 带符号扩展的加载
      emit("EXTEND"); // 扩展到完整的int
      }
      expr->t_type = target_type;
      }

      "我们来测试一下,"我说着,写下了一个测试程序:

      char c = -1;
      int i = c;
      printf("%d\n", i);

      $ cc test.c
      $ ./a.out
      -1

      "完美!"Ritchie看着输出结果说,"现在类型转换正确保留了符号位。"

      我们继续测试了更多的情况:

      void test_conversions() {
      char c1 = -128;
      char c2 = 127;
      int i1 = c1; // 应该是 -128
      int i2 = c2; // 应该是 127
      float f = i1; // 浮点数转换
      // ...
      }

      所有的测试都通过了。这个修复不仅解决了当前的问题,还为C语言后来成为系统编程的标准语言奠定了基础。

      "对了,"Ritchie突然说,"我们还在考虑是否要添加'unsigned'类型..."

      我的心跳漏了一拍。unsigned类型!这是C语言另一个重要的特性。但在我能回答之前,熟悉的眩晕感又来了。

      >>> Mission Completed
      >>> Timeline Stable
      >>> Bug Fixed: Sign Extension in Type Conversion
      >>> Historical Impact: Moderate
      >>> Note: C Language Type System Stabilized
      >>> Preparing for next jump...

      世界再次开始模糊。在消失前,我看到Ritchie在笔记本上写下了什么。也许这就是unsigned类型诞生的时刻。

      每一个bug的修复都在改变着历史,而程序员们却从未知晓。这让我想起了一句古老的谚语:站在巨人的肩膀上。只是在这里,我是在帮助巨人站得更稳。

      # End of Chapter 002
      # Next Timeline Loading...

  • 昵称:
  • 评分: 2分|鲜花一捧 1分|一朵小花 0分|交流灌水 0分|别字捉虫 -1分|一块小砖 -2分|砖头一堆
  • 内容:
  •             注:1.评论时输入br/即可换行分段。
  •                 2.发布负分评论消耗的月石并不会给作者。
  •             查看评论规则>>