为什么C编译器没有在for循环体内给出重新声明错误?

时间:2018-07-11 05:55:03

标签: c++ c for-loop declaration

在C语言中,如果我们这样写:

for(int i = 0; i < 7; i++)
{
    // for loop Body
}

变量i的范围在for循环体内。可以。

但是,如果我这样写:

for(int i = 0; i < 7; i++)
{
    long int i = 1; // Redeclaration of i
}

在这里,变量i在循环体内再次声明,但它成功地编译并在C中运行。

但是,在C ++中,编译器给出了“重新声明'long int i'” 错误。

那么,为什么C编译器没有给出重新声明错误?是编译器错误吗?

3 个答案:

答案 0 :(得分:13)

C ++和C在这里有所区别。根据{{​​3}},强调我的观点:

  

迭代语句是一个块,其范围是的严格子集   其封闭块的范围。 循环主体也是一个块,   scope是迭代语句范围的严格子集。

将其翻译为for循环:

{
    declaration 
    while ( expression) {
        statement
        expression ;
    }
}

您在statement部分中放置的任何内容都可以隐藏声明中引入的任何内容。现在,C ++(17,n4659)在C11 (n1570) §6.8.5 ¶5处明确声明了类似的内容。但它还会继续添加:

  

除非init语句中声明的名称相同   声明区域为条件中声明的区域,

因此,这里第二个i确实是在尝试重新声明。上面的内容听起来令人困惑(在条件中声明了名称!?),但是此处的“条件”是这样定义的([stmt.for]/1):

condition:
    expression
    attribute-specifier-seq decl-specifier-seq declarator brace-or-equal-initializer

正是这样允许while循环这样的([stmt.stmt]/1):

while (T t = x) statement 

C++ only在while循环的语句中。


或者,我得出的全部结论总结在you may not re-declare t中(感谢@ T.C。):

  

如果在初始声明或for-range-声明中引入的名称是   在子语句的最外层块中重新声明,该程序是   格式不正确。

答案 1 :(得分:2)

首先,循环内没有重新声明。在循环体内。 i是在每次进入循环时定义的,作用域仅直到循环体为止。

一旦循环完成迭代,该变量将不再存在。 (首引

话虽如此,您看到的是 shadowing 的结果。 (第二个报价

引用C11

  

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

  

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

答案 2 :(得分:0)

这不是重新声明。 (从字面上看,尽管有些人还是会称呼它)

请仔细阅读...

for(int i = 0; i < 7; i++)
{
    printf("i = %d\n", i);
    int i = 5;
    printf("new i = %d\n", i);
}

以上代码的输出:-

i = 0
new i = 5
i = 1
new i = 5
i = 2
new i = 5
i = 3
new i = 5
i = 4
new i = 5
i = 5
new i = 5
i = 6
new i = 5

很明显,有两个不同的i

较新的i具有更广泛的本地范围。

这是一个错误吗?

目的是什么?

如果不允许这样做,则维护大型项目可能会非常困难,因为您会经常遇到命名冲突。

尽管通常来说,对不同范围内的不同变量使用相同的名称被认为是非常差的做法,但应尽可能避免这样做。

为什么没有警告消息?

使用gcc file_name.c -Wshadow进行编译。


编辑: 您还可以通过在for循环中重新声明原始声明的变量来本地锁定原始声明的变量。