【问题标题】:why the C compiler can't do type inference?为什么 C 编译器不能进行类型推断?
【发布时间】:2012-04-06 08:05:05
【问题描述】:

long double x = 8.99999999999999999 的情况下,该值存储为double,因为未附加“L”。为什么当我已经将变量x 声明为long double 浮点类型时,C 编译器不能进行类型推断?

【问题讨论】:

  • 因为标准 C 规范说它不应该进行类型推断。查看 OcamlHaskell 甚至 C++11 语言规范,了解具有某种类型推断的语言。
  • @BasileStarynkevitch:你可以把它作为答案。
  • 当你已经声明了类型时要推断什么?
  • 这里的限制因素是什么?这是一件很复杂的事情吗?
  • 如果plain double和long double有不同的表示,应该可以直接使用long double表示,因为常量值是已知的。您确定这与您遇到的实际问题相符吗?

标签: c types inference


【解决方案1】:

编译器不进行类型推断,因为the C standard 标准明确规定应该这样做。

关于浮动常量的第 6.4.4.2 节说:

一个无后缀的浮点常量具有双精度类型。如果以字母 f 或 F 为后缀,则具有 类型浮动。如果以字母 l 或 L 为后缀,则为 long double 类型。


标准为什么这么说?也许是因为它简化了编译器,而不是必须这样做(老实说,我不确定)。

【讨论】:

  • 一些 C 编译器 support extensions 用于类型推断,但它们未包含在 C 标准中。
【解决方案2】:

在 1970 年代初期,当 C 语言首次开发时,运行编译器的计算机速度很慢,内存很少。因此,有必要设计语言,使编译器简单,编译速度快。一个简单的编译器需要程序员告诉它一切,因为每次编译器需要推导时,都会占用 CPU 和内存。

另一个,我认为同样重要的原因是,开发和使用 C 的人最初是用它编写操作系统,他们想要一种不试图变得聪明的语言。编写操作系统非常棘手,无需猜测编译器可能会或可能不会做什么:他们需要对发生的事情进行非常精确的控制,从这个意义上说,更简单的语言可能对操作系统编写者有很大的好处。

由于这些原因,C 最终没有许多高级语言在后来的几十年中设计并针对应用程序编程现在拥有的许多特性。它没有面向对象,没有类型推断,没有垃圾收集,没有异常,也不支持线程。

将来 C 可能会被更改为拥有这些东西(事实上,最新的 C 标准现在有本地线程,但我相信它们是可选的),但总的来说,如果你想要这些东西,你想要不同的语言。确实有成千上万种有趣的语言可供选择。

【讨论】:

  • 这都是猜测。我们所知道的是,Dennis Ritchie 基于其 BCPL 传统以这种方式设计了这种语言。另一种解释是,Ritchie 一开始并不是真正的编译器作者,他犹豫着去那个时候角度害怕踩到的地方。
【解决方案3】:

C 编译器不进行类型推断,因为 C 不是类型安全的。您可以轻松地将事物随意转换为 void、void 指针,然后再返回。这并不违反规则。这至少意味着 C 的任何类型推断都只是近似的,并且充其量你会让编译器为你提供关于你的类型出了什么问题的线索。

至于为什么 C 不进行类型推断:C 中的类型不是旨在强制逻辑关系或在语言中编码真理。在某种程度上,具有健全类型系统(Haskell、OCaml、SML、Coq 等)的语言旨在让类型告诉你一些事情:你可以从类型。 (请参阅 Philip Wadler 的“Theorem's for Free!”,这是一个有趣的例子!)

那么为什么 C 使用类型呢?原因纯粹是编译器需要知道——在某种程度上——如何组织你的数据,因为它存储在内存中。 C 中的类型不是逻辑一致性,而是告诉你事物是如何布局的,我应该把这个 int 放在结构中的什么位置,等等......

相反,C 有许多习惯用法来模拟类型安全语言中的更多标准功能。例如,空指针通常用于表示参数多态性。 (例如,您可以拥有一个可以包含指向任何数据类型的指针的列表。)事实上,它做了更多的事情,在 C 中您可以对指向不同数据类型的列表进行编码。虽然在传统的函数式语言中,列表的归纳类型要求所有元素都属于同一类型,但您可以在 C 中轻松地对相交类型和行进行编码(例如,在 C 中,通过使用标识符标记列表元素来完成)。

类型和内存安全的C方言,参见Cyclone作为例子,在某些地方,多态性确实取代诸如 void 指针之类的东西的出现,同时仍然为您提供了 C 语言的许多优点。

【讨论】:

    【解决方案4】:

    在 long double x = 3.0 的情况下。值 3.0 存储为双精度值。

    不是真的!这会将 3.0 存储为 long double

    【讨论】:

      【解决方案5】:

      正如其他人所说的标准状态that unless suffixed, a floating point constant is a double。现在,让我们从计算复杂性来看它背后的基本原理。标准还注意到long double>=double 现在考虑一个系统,其中sizeof(double)=8sizeof(long double)=16。现在,在您没有后缀的特定示例中,编译器只需计算一个 64-bit 精度浮点数并将剩余的 8 个字节归零。但是,如果要按照您的建议进行类型推断,它现在必须进行计算以产生 128-bit 浮点数。

      converting a floating point number into binary 表示并非易事,因此编译器会尽可能地小气。特别是在本例中,无需将8.99999999999999999 升级为long double,因为它可以以合理的精度压缩成double

      【讨论】:

      • 关于斜体的声明:并非如此。 #1) 我在标准中没有看到任何关于此的内容。 #2) 在我的 GCC 副本上,如果我将一个不适合“double”的巨大浮点常量分配给“long double”变量,它会打印为“inf”。只有,正如标准所说,如果我给常量加上一个“L”后缀,它会被正确地存储到变量中。
      • @ArjunShankar 您是否检查过系统上的 double 和 long double 的大小是否相同?我指的是 long double 大于 double 的系统?
      • 是的,我查过了。不,它们不一样。
      • @ArjunShankar 你是对的。我也在另一个双倍为 12 字节的系统上进行了验证,它确实引发了溢出警告。但对我来说,它没有显示警告,但显示了一些垃圾值。 GCC 4.6.1。我还是删除了斜体部分
      猜你喜欢
      • 1970-01-01
      • 2021-03-11
      • 2010-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多