【问题标题】:Clarification on implementation defined behavior in C澄清 C 中实现定义的行为
【发布时间】:2012-11-21 18:29:46
【问题描述】:

作为 C 中实现定义行为的一个示例。C 标准说数据类型的大小是实现定义的。所以,假设 sizeof(int) 是实现定义的。

  1. 此实现定义的行为是否意味着size(int) 是平台相关的或由编译器供应商定义的,或两者兼而有之?

  2. 一旦我编译了我的代码,当我在不同版本的平台上运行它时,实现依赖关系是否仍然适用?在一个平台上编译实现定义的代码并在另一个平台上运行会导致性能损失吗?

【问题讨论】:

  • 很多个帖子正是关于这个(第一个问题)
  • 例如this one

标签: c types implementation


【解决方案1】:

是的,实现定义意味着它依赖于平台(架构 + OS ABI + 编译器)。

是的,实现定义的功能可能因平台的不同版本而异。

【讨论】:

  • 如果我创建我的代码的可执行版本并在不同的架构系统上运行它,大小会改变吗?
  • @AshRj 不,程序根本无法运行,正是因为大小不同。
  • @Let_Me_Be:你的意思是如果我在 Windows XP 上编译某些东西,那么它将无法在 Windows 7 上运行,因为正如你所说的 implementation defined features can differ across different versions of the platform?我会感到惊讶。
  • @user963241 这就是我写“can”而不是“will”的原因。
  • @Let_Me_Be 恐怕在大小成为问题之前,架构就会阻止可执行文件运行。 ;-) 实际上,程序是否可以在不同大小的系统上运行取决于具体情况。以 AMD64 与 x86 为例。
【解决方案2】:

此实现定义的行为是否意味着 size(int) 取决于平台或由编译器供应商定义或两者兼而有之?

原则上编译器供应商可以做出该决定。实际上,如果编译器想要发出直接调用系统库的代码,那么它必须遵循与系统相同的“ABI”(应用程序二进制接口),除此之外,ABI 将指定int 的大小.因此编译器供应商将“决定”使其达到 ABI 所说的大小。

针对多个平台和架构的编译器将作为每个平台配置的一部分单独做出决定。然后每个目标代表一个不同的 C 实现,即使您认为它是“相同的编译器”。

您可以编写一个符合标准的 C 实现,其中 int 的大小与运行程序的操作系统上的大小不同。人们很少这样做,标准库在进行系统调用时必须跳过额外的障碍。它作为模拟器的一部分可能很有用,但你可能会合理地争辩说“平台”是模拟平台,而不是具有不同大小 int 的主机平台。

一旦我编译了我的代码,当我在不同版本的平台上运行它时,实现依赖关系是否仍然适用?

sizeof(int) 是一个编译时常量,这意味着您的编译器发出的代码可能会假定某个值。然后,该二进制代码无法在具有不同大小int 的不同平台版本上正确运行。

在一个平台上编译实现定义的代码并在另一个平台上运行它会导致性能损失吗?

如果它完全有效,那么没有特别的理由可以假设会有性能损失。它通常根本不起作用(见上文),因为通常用于一个平台的二进制代码在另一个平台上不起作用。如果平台足够相似以至于它确实可以工作,那么编译器为一个平台所做的优化可能在另一个平台上并不是那么好的优化。在这种情况下,会出现性能损失,解决方法是重新编译针对正确(版本)平台的代码。

这确实发生在 ARM 上,在 x86 上发生的程度较小。过去不同的芯片提供基本相同的指令集,但某些芯片上的某些指令相对于其他指令具有显着不同的成本。假设指令 X 很快的优化可能是在指令 X 很慢的不同芯片上的糟糕优化。可以想象,这种差异不会让芯片制造商在编译器供应商中大受欢迎,在汇编程序员中更是如此。

【讨论】:

    【解决方案3】:

    此实现定义的行为是否意味着 size(int) 取决于平台或由编译器供应商定义或两者兼而有之?

    在 C 标准术语中,实现是编译器。

    以下是 C 标准中术语实现的实际定义:

    (C99, 3.12p1) 实现: 特定的软件集,在特定的控制选项下运行在特定的翻译环境中,执行程序翻译,并支持特定功能的执行执行环境

    【讨论】:

    • 问:但是编译器受平台操作系统/架构的限制?
    • @GrijeshChauhan 一些实现定义的行为受 ABI 约束。这就是为什么编译器文档有时会针对某些实现定义的行为重定向到 ABI。否则,一些实现定义的行为选择是为了适应硬件而做出的,而另一些则是出于历史原因。
    • @ouah:“实现就是编译器”——我认为这并不完全正确。编译器是您直接访问实现的点,但实现还包含不属于编译器的内容,包括执行程序的环境。
    • @SteveJessop 在某种程度上,是的。 C 用 C99 3.12p1 中的 implementation 术语定义了它的含义。我在回答中添加了一个引号。
    【解决方案4】:

    size(int) 确实依赖于实现。它与性能无关,而与您正在使用的平台的架构有关。 32 位宽的 CPU 的行为与 64 位宽的 CPU 甚至 16 位宽的 CPU 不同。

    这是他们主要指的平台依赖,但也存在交叉编译的问题,这带来了更多的问题。您可以使用 -m 之类的标志来指定架构和宽度,这会导致代码在与最初编译时不同的平台上运行。

    【讨论】:

      【解决方案5】:

      根据C-标准

      ISO/IEC 9899:1999 §3.4.1

      1 实现定义的行为
      未指定的行为,其中每个实现都记录了如何做出选择`

      这意味着编译器中记录的行为是implementation defined

      sizeof() 已记录在案。

      2 示例:实现定义行为的一个示例是高位的传播 当有符号整数右移时。

      附件 J“可移植性问题”包括未指定行为 (J.1)、未定义行为 (J.2)、实现定义行为 (J.3) 和区域设置特定行为 (J.4) 的列表。

      【讨论】:

      • 或者更确切地说:编译器必须记录所有实现定义的行为,否则它不遵循标准。因此,如果您的编译器没有记录例如 sizeof(int) 有多大,那么严格来说,您的编译器不是 C 编译器。
      • @Lundin:编译器的文档指定例如是否合法?对将超出范围的值转换为 int 的结果执行的任何计算都可能产生任意值,或者是否需要文档来指定某些特定的一致行为?
      猜你喜欢
      • 1970-01-01
      • 2015-04-17
      • 2015-08-08
      • 1970-01-01
      • 1970-01-01
      • 2016-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多