编译器无法捕获条件块中的变量重定义

问题描述 投票:0回答:5
int bar = 2;
if (bar)
{
   int bar;
}

gcc 或 Clang 都无法为此发出警告(或错误),并且程序在启动时立即崩溃。这有充分的理由吗?看起来也不是什么难抓的东西。这是块作用域的基础知识:嵌套作用域继承封闭块的名称...

有什么解释吗?

编辑:事实证明崩溃是由于使用 Clang 造成的。我来回测试了很多次,似乎可以肯定是变量重定义和 Clang 的组合导致了崩溃。但是,我无法在测试项目中重现崩溃,所以请自行想象。

问题原来与 Objective-C 相关。正如 Jonathan Leffler 指出的那样,在内部作用域中执行“int bar = bar”会从自身初始化变量,这就是当通过 Objective-C 方法调用完成初始化时导致问题的原因。

以下显示了正在发生的错误:

-(void)crasher
{
   NSNumber* bar = [NSNumber numberWithInt:2];
   if (bar)
   {
      NSString* bar = [self doit:bar];
   }
}

-(NSString*)doit:(NSNumber*)num
{
   NSString* str = [num stringValue];   // This line causes the crash
   return str;
}

请注意,在纯 C 中执行类似的操作不会产生崩溃:

int bar = 2; if (bar) { char buff[10]; int bar = sprintf(buff, "%d",bar); }
    
c objective-c gcc llvm
5个回答
7
投票
这里没什么可抓的。内部块中的变量是一个完全不同的变量,它

隐藏外部块中的变量。这是该语言自古以来就存在的完美标准功能。

您遇到的崩溃与您发布的代码完全无关。除非您在代码中犯了错误,否则请在假设您正在使用外部变量的同时使用内部变量。


4
投票
这是嵌套作用域的基础:在嵌套作用域内,您可以隐藏在外部作用域中声明的内容。 gcc 有一个选项来获取此警告(-Wshadow),但它不会被 -Wall 或 -Wextra 激活:代码中无需更改即可出现警告(标头现在在全局范围内有一个定义)函数中使用的标识符)。


1
投票
扩展Douglas Leeder给出的答案:

#include <stdio.h> static int xx(int foo) { int bar = 2; if (foo > bar) { int foo = bar; int bar = bar; printf("inner: foo = %d, bar = %d\n", foo, bar); } printf("outer: foo = %d, bar = %d\n", foo, bar); return bar; } int main(void) { xx(13); return(0); }

请注意,内部栏是从自身初始化的 - 这会产生未定义的行为。 但在 MacOS X 10.6.2 (GCC 4.2.1) 上我得到:

inner: foo = 2, bar = 0 outer: foo = 13, bar = 2


变体 1:堆栈践踏 - A

有趣的是,无论我是在

i

 之前还是之后声明 
a
,我都会从这段代码中获得相同的输出,并使用堆栈遍历函数。

inner: foo = 2, bar = 20 outer: foo = 13, bar = 2

代码:

#include <stdio.h> static void modify_stack(void) { int a[20]; int i; for (i = 0; i < 20; i++) { a[i] = 0xFFFFFFFF ^ i; printf("a[i] = 0x%08X\n", a[i]); } } static int xx(int foo) { int bar = 2; if (foo > bar) { int foo = bar; int bar = bar; printf("inner: foo = %d, bar = %d\n", foo, bar); } printf("outer: foo = %d, bar = %d\n", foo, bar); return bar; } int main(void) { modify_stack(); xx(13); return(0); }

由于行为未定义,所以这个结果很好。

变体 2:堆栈践踏 - B

#include <stdio.h> static int modify_stack(void) { int a[20]; int i; for (i = 0; i < 20; i++) { a[i] = 0xFFFFFFFF ^ i; printf("a[i] = 0x%08X\n", a[i]); } i = a[13]; return(i); } static int xx(int foo) { int bar = 2; if (foo > bar) { int foo = bar; int bar = bar; printf("inner: foo = %d, bar = %d\n", foo, bar); } printf("outer: foo = %d, bar = %d\n", foo, bar); return bar; } int main(void) { int i = modify_stack(); xx(13); return(i & 0xFF); }

输出(除了循环打印的数据):

inner: foo = 2, bar = -14 outer: foo = 13, bar = 2
    

1
投票
$ gcc 1.c $ gcc -Wall 1.c 1.c: In function ‘main’: 1.c:6: warning: unused variable ‘bar’ $ cat 1.c int main() { int bar = 2; if (bar) { int bar; } return 0; } $ ./a.out ; echo $? 0

为我编译 - 在 -Wall 下有警告。并且程序运行正常。

您声明了两个变量,一个称为

bar

,另一个也称为 
bar
。编译器不在乎,只要它们位于不同的作用域即可。


1
投票
这也是块作用域的基础,在内部作用域中从外部作用域重新定义名称并不是错误——这是正常的且是预期的。否则基本上会让我们回到只有全局变量的古代 BASIC 方言的糟糕时代。

© www.soinside.com 2019 - 2024. All rights reserved.