此代码将编译并在当前C标准下定义良好:
static int foo(int);
extern int foo(int);
该标准规定在这种情况下( C11:6.2.2标识符的链接(p4)):
对于使用存储类说明符extern声明的标识符 可以看到该标识符的先前声明的范围, 31) 如果事先声明指定内部或外部联系,则 后面声明中标识符的链接与 在先前声明中指定的联系。 [...]
...这意味着int foo(int)
函数被声明为static int foo(int)
。
像这样交换这些声明:
extern int foo(int);
static int foo(int);
...使用GNU GCC给我一个编译器错误:
静态声明' foo'遵循非静态声明
我的问题是:第二种情况背后的设计理由是错误,而不是以与第一种情况类似的方式处理?我怀疑这与单独的翻译单元更易于管理和#include
这一事实有关?我觉得如果不理解这一点,我可以在未来的C项目中面对一些错误。
答案 0 :(得分:3)
我可以想象导致这种不对称的情况是全局范围内标识符的默认链接为extern
。 1 无法标记<先前静态声明的函数static
的em> definition 也将是错误,因为默认情况下它也是extern
声明。
这里是一个例子。在现代C语言中,必须在使用函数之前声明函数,但有时实现是在最后,因为它是文件中代码的主要目的之后的>
static void helper_func(); // typically not in a header
// code using helper_func()
// And eventually its definition, which by default
// declares an **external** function. Adding
// an explicit `extern` would not change a thing; it's redundant.
void helper_func() { /* ... */ }
这看起来足够无辜,意图很明确。当C标准化时,大概会有这样的代码。这就是为什么允许它。
现在考虑相反的情况:
extern void func(); // this could be in a header
// ... intervening code, perhaps a different file ...
static void func() { /* ... */ }
// code which uses func()
很明显,这是不允许的。定义函数static
是一个清晰明确的声明。先前相互矛盾的extern
声明没有任何意义。 2 很可能这是无意间发生的名称冲突,例如在标头中声明的函数。在形式化时,大概没有多少代码看起来像这样。这就是为什么它被禁止。
1 从C17草案6.2.2 / 5:“如果一个函数的标识符声明没有存储类说明符,则其链接的确定就如同确定它的链接一样用存储类说明符extern声明。”
2 可能会争辩说,应该禁止使用关键字的显式 extern
声明以及后面的static
定义而仍然可以允许不带关键字的隐式变量(并且相应地,应禁止在extern
声明之后的显式 static
函数定义,而<仍然允许使用em> implicit 。但是标准应该做到的分裂头发是有局限的(而且已经走得很远了)。
答案 1 :(得分:1)
我认为这个令人困惑的规范的想法是,可以在函数内部使用extern
声明来引用全局函数或对象,例如将其与具有相同名称的另一个标识符消除歧义
static double a; // a declaration and definition
void func(void) {
unsigned a;
.....
if (something) {
extern double a; // refers to the file scope object
}
}
然而,如果您使用static
,则声明新内容:
extern double a; // just a declaration, not a definition
// may reside elsewhere
void func(void) {
unsigned a;
.....
if (something) {
static double a; // declares and defines a new object
}
}