【问题标题】:Why does the following code give different results when compiling with gcc and g++?为什么以下代码在使用 gcc 和 g++ 编译时会给出不同的结果?
【发布时间】:2015-03-26 06:47:35
【问题描述】:
#include<stdio.h>
int main()
{
    const int a=1;
    int *p=(int *)&a;
    (*p)++;
    printf("%d %d\n",*p,a);
    if(a==1)
      printf("No\n");//"No" in g++.
    else
      printf("Yes\n");//"Yes" in gcc.
    return 0;
}

以上代码在g++ 编译中给出No 作为输出,在gcc 编译中给出Yes。谁能解释一下这背后的原因?

【问题讨论】:

  • 您是在问“为什么允许这种情况发生?”或“我知道任何事情都可以发生,但是编译器内部的什么机制使这个特殊的事情发生了?”?
  • @immibis 我猜是后者“我知道任何事情都可以发生,但是编译器内部的什么机制让这件事发生了?”
  • @RohitChauhan ,阅读答案。
  • 您使用的是g++gcc 的哪个版本?另外,您使用了哪些命令行选项来编译代码?我希望g++a 替换为if 中的文字常量1,但gcc 不是。
  • @MohitJain 不是真的

标签: c gcc g++ constants


【解决方案1】:

您的代码触发了未定义的行为,因为您正在修改 const 对象 (a)。它不必产生任何特定的结果,即使在相同的平台上,使用相同的编译器。

虽然未指定此行为的确切机制,但您可以通过检查代码生成的程序集来找出在您的特定情况下发生的情况(您可以通过使用 -S 标志看到这一点。 ) 请注意,允许编译器通过假设代码具有明确定义的行为来进行积极的优化。例如,a 可以简单地替换为 1,无论在哪里使用。

【讨论】:

    【解决方案2】:

    来自 C++ 标准(1.9 程序执行)

    4 本国际中描述了某些其他操作 标准未定义(例如,尝试 修改一个 const 对象)。 [注:本国际标准规定 对包含未定义的程序的行为没有要求 行为。 ——尾注]

    因此您的程序具有未定义的行为。

    【讨论】:

      【解决方案3】:

      在您的代码中,请注意以下两行

      const int a=1;        // a is of type constant int
      int *p=(int *)&a;     // p is of type int *
      

      您将const int 变量的地址放入int *,然后尝试修改该值,该值应该被视为const。这是不允许的,会调用undefined behaviour

      供您参考,如第 6.7.3 章,C11 标准,第 6 段所述

      如果尝试通过使用来修改使用const 限定类型定义的对象 具有非const 限定类型的左值,则行为未定义。 如果尝试是 通过使用左值来引用使用 volatile 限定类型定义的对象 对于非 volatile 限定类型,行为未定义

      因此,长话短说,您不能依赖输出来进行比较。它们是未定义行为的结果。

      【讨论】:

      • 你知道它是什么时候进入 C 语言的吗?因为它从过去显着改变了 const 变量的定义。在编译器必须证明 const 标记的变量在其他地方没有被更改之前,它才能重用一个副本。在这个版本中,它已被重新定义为基本上是 'restrict' 的别名,因此编译器现在将假定该变量在其范围内的整个时间内都不会被别名。这似乎会降低“const”的用处,甚至可能会因为它调用“不合理的行为”而避免某些事情。
      【解决方案4】:

      好的,我们在这里将“相同”的代码传递给“相同”的编译器,但只有一次 带有 C 标志,另一次带有 C++ 标志。只要合理 用户担心没有任何改变。代码应该被解释 编译器也一样,因为没有发生任何重要的事情。

      实际上,这不是真的。虽然我很难指出它 一个标准,但对 'const' 的精确解释略有不同 在 C 和 C++ 之间。在 C 语言中,它是一个附加组件,'const' 标志 表示代码不应写入此正常变量“a” 绕到这里。但是有可能它会被写入 别处。对于 C++,重点更多是不可变常量 概念和编译器知道这个常量更类似于 'enum' 是一个普通的变量。

      所以我预计这种细微的差异意味着解析略有不同 生成的树最终导致不同的汇编器。

      这种事情实际上是相当普遍的,在 C/C++ 中的代码 即使使用子集也不总是编译为完全相同的汇编程序 “相同”的编译器。它往往是由其他语言特性引起的 意味着有些事情你无法证明代码的正确性 现在是其中一种语言,但在另一种语言中没问题。

      通常 C 是性能赢家(正如 Linux 重新发现的那样 kernel devs),因为它是一种更简单的语言,但在这个例子中,C++ 可能会更快(除非 C 开发切换到宏 或枚举 并捕捉到获取不可变常量地址的不合理行为。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-14
        • 1970-01-01
        • 2015-02-08
        • 1970-01-01
        • 1970-01-01
        • 2016-11-09
        相关资源
        最近更新 更多