函数上的存储类说明符不一致

PSk*_*cik 6 c language-lawyer

我有

static void static_func(void);
void static_func(void) { }
static inline void static_inline_func(void);
void static_inline_func(void) { }

#if 0 //error
    void extern_func(void){}
    static void extern_func(void);
#endif

int main()
{
    static_func();
    static_inline_func();
}
Run Code Online (Sandbox Code Playgroud)

我期待警告或错误,但是即使使用,gcc和clang也都没有做出任何警告-Wall -Wextra -pedantic

这是合格的C吗?为什么或者为什么不?

Eri*_*hil 8

关于:

static void static_func(void);
void static_func(void) { }
Run Code Online (Sandbox Code Playgroud)

对于第一行,C 2018 6.2.2 3说:

如果对象或函数的文件作用域标识符的声明包含存储类说明符static,则该标识符具有内部链接。

对于第二行,6.2.2 5表示:

如果函数标识符的声明没有存储类说明符,则其链接的确定与使用存储类说明符声明时完全相同extern

因此,我们参考有关何时extern指定的段落,即6.2.2 4(加重):

对于extern 在可见该标识符的先前声明的范围内用存储类说明符声明的标识符,如果该先前声明指定了内部或外部链接,则该标识符在后面的声明中的链接与指定的链接相同在事先声明。。。

因此,由于先前的声明是可见的,void static_funct(void)因此等效于static void static_funct(void)

(请注意,对象的6.2.2 5有所不同;在上面引用的部分之后,它继续说:“如果对象标识符的声明具有文件范围且没有存储类说明符,则其链接是外部的。”因此static int x; int x;创建对象发生冲突x,而static int f(void); int f(void);不会为函数造成冲突f。)

关于:

static inline void static_inline_func(void);
void static_inline_func(void) { }
Run Code Online (Sandbox Code Playgroud)

inline 是一个功能说明符,而不是一个存储类说明符,而6.7.4 6告诉我们:

inline函数说明符声明的函数是内联函数

正如我们在上面看到的void static_inline_func(void) { },由于之前的声明,它仍然具有内部链接。6.7.4 7对于具有内部链接的内联函数的要求很松懈:

具有内部链接的任何功能都可以是嵌入式功能。…

如果未使用声明函数static,则在6.7.4 7中还有其他限制:

对于具有外部链接的功能,适用以下限制:如果使用inline功能说明符声明功能,则还应在同一转换单元中对其进行定义。如果翻译单元中某个函数的所有文件范围声明都包含inline不带的函数说明符extern,则该翻译单元中的定义为内联定义。内联定义不为函数提供外部定义,也不禁止在另一个翻译单元中使用外部定义。内联定义提供了外部定义的替代方法,翻译器可以使用该外部定义在同一翻译单元中实现对该函数的任何调用。…

(尚不清楚“以下限制适用于”的文本在何处结束。)

因此,可能同时存在一个函数的内联定义和一个外部定义(在另一个翻译单元中)。在这两种情况下,似乎都没有禁止同时声明和不声明函数的禁止inline。这样做仅意味着含义,特别是声明有或没有inline手段的外部函数都意味着该翻译单元中的定义不是内联定义。