C 嵌套范围变量情况下的异常行为

C 嵌套范围变量情况下的异常行为,c,gcc,scope,C,Gcc,Scope,我熟悉C语言中的嵌套作用域规则,其中嵌套块中的相同变量名会遮挡具有相同名称的外部变量。 但是对于下面的代码片段,我无法确定输出的解释 #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("

我熟悉C语言中的嵌套作用域规则,其中嵌套块中的相同变量名会遮挡具有相同名称的外部变量。 但是对于下面的代码片段,我无法确定输出的解释

#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;
}
#包括
int main()
{
int x=1,y=2,z=3;
printf(“x=%d,y=%d,z=%d\n”,x,y,z);
{
int x=10;
浮动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);
}
返回0;
}
上述代码段的输出为:

x=1,y=2,z=3

x=10,y=20.000000,z=3

x=1,y=20.000000,z=2

请大家帮助我理解第三个printf语句中的
y
值是如何产生超出范围的变量值的

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


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

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

在第一个内部范围内

{
    int x = 10;
    float y = 20;

    printf(" x = %d, y = %f, z = %d \n", x, y, z);
}
 printf(" x = %d, y = %f, z = %d \n", x, y, z);
                  ^^^^^^
y
被(重新)定义为
float
,使用该变量作为
%f
转换说明符的参数是完全正确的。但是,一旦作用域结束,(visble)
y
再次成为
int
。因此在第二个内部范围内

{
    int x = 10;
    float y = 20;

    printf(" x = %d, y = %f, z = %d \n", x, y, z);
}
 printf(" x = %d, y = %f, z = %d \n", x, y, z);
                  ^^^^^^
使用
%f
是错误的,并会导致错误。必须使用
%d
在此处打印
y
的值

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

每个其他标识符的作用域由其声明的位置决定(在 声明符或类型说明符)

[……]

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

[……]

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

以及与UB相关的第§7.21.6.1章

[…]如果有任何论点 对应转换规范的类型不正确,行为为 未定义


您有一个未定义的行为:

{
    int z = 100;
    printf(" x = %d, y = %f, z = %d \n", x, y, z); // here y is an int
}
如果
printf()
中的标志与良好类型不匹配,则这是未定义的bahavior

您必须使用
%d
标志

{
    int z = 100;
    printf(" x = %d, y = %d, z = %d \n", x, y, z); // here y is an int
}
首先,我认为它可能是垃圾值,因为使用%f打印整数会导致垃圾值,因此将内部范围中的y值更改为其他值会导致与输出相同的值,因此我确认它不是垃圾值

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

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

{
    int z = 100;
    printf(" x = %d, y = %f, z = %d \n", x, y, z); // here y is an int
}
C中未定义的行为
C是一种非常低级的语言,其结果如下:

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

许多语言,特别是针对某些托管环境的语言,如
Java
或者当你做了不被允许的事情时,比如说, 访问不存在的数组元素<代码>C
没有。只要你 程序语法正确,编译器不会抱怨。如果你这样做 程序中禁止的内容,
C
只调用 程序未定义。这正式允许在运行时发生任何事情 节目。通常,结果将是崩溃或只是“垃圾”的输出 值,如上所示。但是如果你真的不走运,你的程序看起来会 直到它得到一些稍微不同的输入,然后 一段时间后,你将很难找到你的程序的确切位置 未定义。因此,请务必避免未定义的行为

另一方面,未定义的行为也可能导致安全漏洞。这 在实践中发生了很多事情

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

但它是未定义的,作为证明,请使用您未修改的代码查看我的机器上发生的情况:

$/范围
x=1,y=2,z=3
x=10,y=20.000000,z=3
x=1,y=0.000000,z=3


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

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

问题如其他答案所示。您忘记了在范围内
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;
}
#包括
int main()
{
int x=1,y=2,z=3;
printf(“x=%d,y=%d,z=%d\n”,x,y,z);
{
int x=10;
浮动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);
}
返回0;
}
未定义的行为