【问题标题】:Unusual behaviour in case of nested scope variables [duplicate]嵌套范围变量的异常行为[重复]
【发布时间】:2017-11-16 10:09:36
【问题描述】:

我熟悉 C 中的嵌套范围规则,其中嵌套块内的相同变量名称会隐藏具有相同名称的外部变量。 但是对于以下代码 sn-p 我无法确定输出的解释。

#include <stdio.h>
int main()
{
    int x = 1, y = 2, z = 3;
    printf(" x = %d, y = %d, z = %d \n", x, y, z);
    {
        int x = 10;
        float y = 20;

        printf(" x = %d, y = %f, z = %d \n", x, y, z);
    }

    {
        int z = 100;
        printf(" x = %d, y = %f, z = %d \n", x, y, z);
    }

    return 0;
}

上面sn-p的输出是:

x = 1,y = 2,z = 3

x = 10,y = 20.000000,z = 3

x = 1,y = 20.000000,z = 2

谁能帮我理解第三个 printf 语句中 y 的值如何产生超出范围的变量值。

首先我认为它可能是垃圾值,因为使用%f 打印整数会导致垃圾值,因此将内部范围内的y 的值更改为其他值会导致与输出相同的值,因此我' m 确认它不是垃圾值。

我使用 gcc 版本 6.3.1 20161221 (Red Hat 6.3.1-1) (GCC) 编译程序,以及使用各种在线编译器编译程序。

【问题讨论】:

  • 你知道为什么z的值在第三个范围内没有更新为100吗?
  • 无法复制。
  • 鉴于下面的答案,我认为可以肯定地说您遇到了复制粘贴错误...
  • 您正在尝试使用 %f 打印整数。我相信编译器会抱怨的。但我也确信您一定禁用了警告。
  • @GauravPathak 不,我在发布问题时没有,但现在感谢 Paul Ogilvie 的回答,我知道了。

标签: c gcc scope


【解决方案1】:

你有一个未定义的行为:

{
    int z = 100;
    printf(" x = %d, y = %f, z = %d \n", x, y, z); // here y is an int
}

如果printf() 中的标志与好的类型不匹配,这是未定义的行为。

您必须使用%d 标志。

{
    int z = 100;
    printf(" x = %d, y = %d, z = %d \n", x, y, z); // here y is an int
}

【讨论】:

    【解决方案2】:

    问题在于格式说明符和提供的参数不匹配。让我们仔细看看。

    在第一个内部作用域

    {
        int x = 10;
        float y = 20;
    
        printf(" x = %d, y = %f, z = %d \n", x, y, z);
    }
    

    y 被(重新)定义为float 并使用该变量作为%f 转换说明符的参数非常好。但是,一旦作用域结束,(可见y 又是一个int。因此在第二个内部范围内

     printf(" x = %d, y = %f, z = %d \n", x, y, z);
                      ^^^^^^
    

    使用%f 是错误的,会导致undefined behavior。您必须使用%d 来打印y 的值。

    相关,引用C11,第 §6.2.1/P4 章

    每个其他标识符的范围由其声明的位置决定(在 声明符或类型说明符)。

    [...]

    如果声明符或类型说明符 声明标识符出现在块内或参数声明列表中 一个函数定义,标识符有块作用域,它终止于 关联块。

    [....]

    如果一个标识符指定了两个不同的同名实体 空间,范围可能重叠。如果是这样,一个实体的范围(内部范围)将结束 严格在其他实体的范围之前(外部范围)。在内部范围内, 标识符指定在内部范围内声明的实体;在外部声明的实体 范围在内部范围内隐藏(不可见)。

    并且,与 UB 相关,来自第 §7.21.6.1 章

    [...] 如果有任何参数 不是相应转换规范的正确类型,行为是 未定义。

    【讨论】:

      【解决方案3】:

      首先我认为它可能是垃圾值,因为使用 %f 打印整数会导致垃圾值,因此将内部范围内的 y 值更改为其他值会导致与输出相同的值,因此我确认它不是垃圾值。

      这是一个错误的假设。它“垃圾”,因为您在这里有未定义的行为

      我引用自己对未定义行为的解释:

      C 中的未定义行为

      C 是一种非常低级的语言,其结果如下:

      没有什么能阻止你做完全错误的事情

      许多语言,尤其是那些用于某些托管环境的语言,例如JavaC# 在您做不允许的事情时阻止您,例如, 访问不存在的数组元素。 C 没有。只要你的 程序语法正确,编译器不会抱怨。如果你这样做 你的程序中禁止的东西,C 只是调用你的行为 程序未定义。这正式允许在运行时发生任何事情 该程序。通常,结果将是崩溃或只是“垃圾”的输出 值,如上所示。但如果你真的不走运,你的程序看起来 工作得很好,直到它得到一些稍微不同的输入,然后 时间,您将很难确定您的程序的确切位置 未定义。因此一定要避免未定义的行为!

      附带说明,未定义的行为也可能导致安全漏洞。这 在实践中发生了很多

      您遇到了一种情况,即程序似乎做了一些类似于正确执行的事情,这是运气不好

      但它是未定义的,作为证据,看看我的机器上使用你未修改的代码会发生什么:

      $ ./scope
       x = 1, y = 2, z = 3
       x = 10, y = 20.000000, z = 3
       x = 1, y = 0.000000, z = 3
      

      附注:虽然 shadowing 是明确定义的,但您也应该避免它。创建带有阴影的错误代码是有风险的,因为尽管编译器没有问题,开始混淆你的变量。

      【讨论】:

        【解决方案4】:

        其他答案中确定的未定义行为在我的英特尔平台上使用 VC 进行了解释,因为 %f 格式说明符在堆栈上需要一个双精度值,它大于 int 所以当它检索值时,它检索的字节数比 int 的多,现在假定下一个参数位于堆栈上的不同位置,导致 z 打印错误(即打印的不是 z)。

        【讨论】:

        • 感谢您的洞察,现在我明白了 y 值的原因,我完全没有注意到 z 的值也是错误的。
        【解决方案5】:

        试试下面代码中的方法! ;)

        问题如其他答案所示。您忘记了在范围内 y 变量是 int!!!

        #include <stdio.h>
        
        int main()
        {
            int x = 1, y = 2, z = 3;
            printf(" x = %d, y = %d, z = %d \n", x, y, z);
            {
                int x = 10;
                float y = 20;
        
                printf(" x = %d, y = %f, z = %d \n", x, y, z);
            }
        
            {
                int z = 100;
                printf(" x = %d, y = %f, z = %d \n", x, (float)y, z);
            }
        
            return 0;
        }
        

        【讨论】:

          猜你喜欢
          • 2019-08-21
          • 2011-07-10
          • 2016-09-25
          • 1970-01-01
          • 2013-05-26
          • 2011-03-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多