使用超出范围的声明

noi*_*put 5 c gcc clang

码:

void foo() {
    extern int a;
    extern void b(int);
}

void bar() {
    b(9); // ok, warning: use of out-of-scope declaration of 'b'
    a=9; // error: use of undeclared identifier 'a'
}
Run Code Online (Sandbox Code Playgroud)

为什么编译器不只是发出警告use of out-of-scope declaration of 'a'?

zwo*_*wol 5

这是因为隐含的函数声明的残留特征.如果你有的话

void bar()
{
    b(9);    
}
Run Code Online (Sandbox Code Playgroud)

这实际上是100%有效的标准前 C(好吧,除了void当时不存在,但现在不重要)相当于写作

void bar()
{
    extern int b();
    b(9);    
}
Run Code Online (Sandbox Code Playgroud)

(请记住,函数声明中的空参数列表并不意味着该函数采用零参数.这意味着该函数采用了未指定数量的参数.)

现在,当你有

void foo()
{
    extern void b(int);
}

void bar()
{
    b(9);
}
Run Code Online (Sandbox Code Playgroud)

隐式声明意味着它就像你写的那样

void foo()
{
    extern void b(int);
}

void bar()
{
    extern int b();
    b(9);
}
Run Code Online (Sandbox Code Playgroud)

外部符号的两个声明b不兼容.如果它们都在范围内可见,那么bar这将是一个约束违规("X是一个约束违规"是C标准最接近的说法"一个执行X的程序无效且编译器必须拒绝它") .但它们不是,所以相反,程序的含义是不确定的.clang似乎决定从foo范围中应用声明,但也警告你,这对我来说似乎是公平的.gcc会将其视为错误:

test.c:6:5: error: incompatible implicit declaration of function ‘b’
     b(9);
     ^
test.c:2:17: note: previous implicit declaration of ‘b’ was here
     extern void b(int);
                 ^
Run Code Online (Sandbox Code Playgroud)

("以前的隐含声明"并不完全正确,但现在也不重要.)

你也许可以看到这样的事情怎么可能会导致难以发现的错误,这就是为什么现代最好的做法是在声明与外部链接的东西只在文件范围内,以饱满的原型声明的所有功能.

只隐式声明了函数,这就是为什么a给出了一个很难的错误; 在你的原始例子中,a完全不可见bar.(请注意,如果您使用其他类型重新声明它,那么也会使程序的含义未定义.)