【问题标题】:Output difference in gcc and turbo Cgcc和turbo C的输出差异
【发布时间】:2011-03-18 09:21:53
【问题描述】:

为什么使用gccturbo c这两个编译器编译代码时产生的输出会有差异。

#include <stdio.h>

int main()
{    
    char *p = "I am a string";
    char *q = "I am a string";

    if(p==q)
    {
        printf("Optimized");
    }
    else{
        printf("Change your compiler");
    }
    return 0;
}

我在gcc 上得到"Optimized",在turbo c 上得到"Change your compiler"。为什么?

【问题讨论】:

  • 接受提示;使用 gcc ;-)
  • 看到您的问题的答案已经包含在printf 字符串中(顺便说一句,那里缺少\n)我想您确实从某个地方得到了这个代码示例?这是作业吗?
  • FWIW,如果你说的是 Borland 的古董编译器,我认为它有一个命令行选项 (-d) 来合并字符串常量。
  • 实际上,Turbo C 是一个相当不错的小型编译器,具有高效的 IDE、平庸的项目管理和可用的调试器。它可以生成漂亮、干净的 .COM 或实模式 .EXE 可执行文件,这很好,因为嵌入式 80x86/ISA 或 PC-104 平台仍在工业应用中使用。如果您仍然需要为 MS-DOS 开发,这是一个了不起的选择。
  • @Luther Blissett:我不认为 OP 需要 为 MS-DOS 开发。问题看起来像家庭作业/初学者的东西。在这种情况下,为新平台生成代码的编译器可能是更好的选择。

标签: c++ c string optimization compiler-construction


【解决方案1】:

您的问题已被标记为 C 和 C++。所以我会同时回答这两种语言。

[C]

来自 ISO C99 (Section 6.4.5/6)

It is unspecified whether these arrays are distinct provided their elements have the appropriate values.

这意味着unspecifiedpq 是否指向相同的字符串文字。在gcc 的情况下,它们都指向"I am a string"(gcc 优化您的代码),而在turbo c 中它们不是。

未指定的行为: 使用未指定的值或本国际标准提供的其他行为 两种或多种可能性,并且对在任何选择中的选择没有进一步的要求 实例


[C++]

来自 ISO C++-98 (Section 2.13.4/2)

Whether all string literals are distinct(that is, are stored in non overlapping objects) is implementation defined.

在 C++ 中,您的代码调用实现定义的行为。

实现定义的行为: 未指定的行为,每个实现documents 如何做出选择


另请参阅this问题。

【讨论】:

  • +1 报告了“未指定”/“实施定义”行为的主观标准定义含义。
  • 谢谢@Shin 和@Amardeep :)
【解决方案2】:

由于您的字符串文字是一个常量表达式,即您不应该通过指针修改它,因此将其两次存储在内存空间中没有真正的目的。作为较新的编译器,gcc 默认会合并文字,而 Turbo C 则不会。这是 gcc 支持具有 const 数据概念的新语言标准的标志。

【讨论】:

  • 您可以在 gcc 中通过传递 -fno-merge-constants 选项来覆盖此行为,但通常没有充分的理由这样做。
  • @Amardeep,您的回答并不完全正确。字符串文字不是常量表达式,否则无法将其分配给char*。确实,一个不应该然后通过指针访问来改变它,但它是允许的。这种行为是不确定的......无论如何,我不明白人们在分配这样的作业时表现出如此坏的习惯。这应该始终是一个char const*,字符串文字的这种地址被分配到该地址。
  • @Jens:由于早期的 C 编译器没有 const 的概念,因此您只需将 char * 分配给它,即使针对 ROM 的编译器通常将字符串留在只读内存中而不是复制它在程序加载时进入 RAM。为了便于携带,将它们视为不可变总是更安全。较新的编译器当然将它们视为不可变的,否则默认的合并行为将是不安全的。
  • “由于您的字符串文字是一个常量表达式,即从技术上讲,您不允许通过指针修改它”。但是,术语“常量表达式”可能会与形式概念混淆。您可以使用指针修改一些常量表达式。 C++ 和 C 中的“常量表达式”是指表达式的某些特性可以在编译时确定(它的值(例如:整型和整型常量表达式)、它的引用地址(例如:地址和引用常量表达式)及其成员偏移量(示例:指向成员常量表达式的指针))。
  • @Jens 正式地,字符串文字是“常量表达式”。非正式地,在 C++ 中,字符串文字实际上是“const char [N]”类型,因此实际上是具有 const 限定类型的表达式。一个特殊的向后兼容到 C 的转换负责转换到 char* 是可能的。该转换将消失,并且此类转换在 C++0x 中格式不正确。另外,我不会称其为“允许的”。这样做是未定义的行为,但如果所有产生未定义行为的东西都被允许,那么 C 仍然会禁止的东西很少。
【解决方案3】:

请忘记在同一行的答案

“这是因为 Turbo C 太老了,他们当时做不到,因为它必须很快,但 GCC 是全新的和 RAD,这就是它这样做的原因!”

两个编译器都支持合并字符串常量作为一个选项。 GCC 选项 (-fmerge-constants) 在优化级别打开,而 Turbo C 选项 (-d) 默认关闭。如果您使用的是 TCC IDE,请转到 Options|Compiler...|Code Generation.. 并检查“Duplicate strings merged”。

【讨论】:

  • 我发现您的答案难以阅读并且最初完全误解了它,因为引用不是很清楚地识别出来。我希望您对我的格式更改感到满意。除此之外,对于仍在与 TC 打交道的任何人来说,这都是很好且有用的信息,所以:+1。
  • 哦,那好多了。谢谢!
【解决方案4】:

来自 gcc 手册页:

-fmerge-constants

尝试合并相同的常量(字符串常量和 浮点常数)跨越 编译单元。

此选项是优化编译的默认选项,如果汇编器 和链接器支持它。采用 -fno-merge-constants 来禁止这种行为。

在 -O、-O2、-O3、-Os 级别启用。

因此输出。

【讨论】:

    【解决方案5】:

    Turbo C 已针对快速编译进行了优化,因此它没有任何会减慢速度的功能。识别重复的字符串会减慢速度,即使只是轻微的。

    【讨论】:

    • 我认为这个解释是错误的。 Turbo C 的默认设置只是为了允许修改字符串常量的损坏代码默认工作。
    【解决方案6】:

    如果认为合适,编译器可能会保留两个相同文字的副本。找出是否是这种情况大概是这个程序的重点。

    在过去的美好时光中,汇编程序将所有文字保存在文字池中,修补文字池是一种公认​​的(如果未获批准)在整个程序中修改“常量”的技术。

    如果编译器在这种情况下允许*p = 'H';,那么会导致重要的行为差异。

    【讨论】:

    • 应该说,在 C 的许多早期(ANSI 之前)版本中,允许修改文字字符串。
    • @JeremyP:定义“允许”。我很确定它总是未定义的行为(嵌入式系统可以将该字符串放在 ROM 中)(尽管从技术上讲,在 ANSI 之前,每个都是正式的“未定义行为”)
    • 嵌入式系统的编译器通常会为用户提供非常精细的控制,让他们知道去哪里做什么。字符串文字不太可能进入 ROM,而您对此无能为力。
    • 从某种意义上说是允许的,尽管在 K & R C 中没有这样指定,但您可以隐式地更改文字字符串。一些编译器甚至包含在启动时将字符串文字从文本段复制到数据段的代码,作为程序初始化的一部分。
    • @Luther:你总是可以做一些事情,正确和便携的方式。 char mystring[] = "literal goes here"; 然后使用mystring 而不是"literal goes here"
    【解决方案7】:

    历史脚注:由于地址小于浮点数字常量,因此 FORTRAN 用于处理浮点常量,就像 C 处理字符串一样。由于内存是宝贵的,相同的常量将被分配相同的空间。此外,参数传递总是通过引用完成的。这意味着,如果将一个数字常量传递给一个修改其参数的过程,那么该“常量”的其他出现将改变值。

    因此有句老话:“变量不会;常量不会。”

    顺便说一句,有没有人注意到 Turbo C 2.0 printf 中的错误,当使用像“%1.1f”这样的格式来打印像 99.99(输出 00.0)这样的数字时会失败?在 2.01 中修复,它让我想起了 Windows 3.1 计算器的错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-14
      • 2015-02-02
      • 2011-09-22
      • 2013-12-25
      • 2020-11-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多