I have
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();
}
I was expecting warnings or errors but neither gcc nor clang are making any, even with -Wall -Wextra -pedantic
.
Is this conformant C? Why or why not?
Regarding:
static void static_func(void);
void static_func(void) { }
For the first line, C 2018 6.2.2 3 says:
If the declaration of a file scope identifier for an object or a function contains the storage-class specifier
static
, the identifier has internal linkage.
For the second line, 6.2.2 5 says:
If the declaration of an identifier for a function has no storage-class specifier, its linkage is determined exactly as if it were declared with the storage-class specifier
extern
.…
so we refer to the paragraph about when extern
is specified, 6.2.2 4 (emphasis added):
For an identifier declared with the storage-class specifier
extern
in a scope in which a prior declaration of that identifier is visible, if the prior declaration specifies internal or external linkage, the linkage of the identifier at the later declaration is the same as the linkage specified at the prior declaration.…
Thus, since the prior declaration is visible, void static_funct(void)
is equivalent to static void static_funct(void)
.
(Note that 6.2.2 5 differs for objects; after the portion quoted above, it goes on to say “If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external.” Thus static int x; int x;
creates a conflict for the object x
, whereas static int f(void); int f(void);
does not create a conflict for the function f
.)
Regarding:
static inline void static_inline_func(void);
void static_inline_func(void) { }
inline
is a function specifier, not a storage-class specifier, and 6.7.4 6 tells us:
A function declared with an
inline
function specifier is an inline function.…
As we saw above, void static_inline_func(void) { }
still has internal linkage, due to the prior declaration. 6.7.4 7 is quite lax about the requirements on inline functions with internal linkage:
Any function with internal linkage can be an inline function.…
Had the function not be declared with static
, there are further restrictions in 6.7.4 7:
For a function with external linkage, the following restrictions apply: If a function is declared with an
inline
function specifier, then it shall also be defined in the same translation unit. If all of the file scope declarations for a function in a translation unit include theinline
function specifier withoutextern
, then the definition in that translation unit is an inline definition. An inline definition does not provide an external definition for the function, and does not forbid an external definition in another translation unit. An inline definition provides an alternative to an external definition, which a translator may use to implement any call to the function in the same translation unit.…
(It is not clear where the text covered by “the following restrictions apply” ends.)
Thus, there may be both an inline definition of a function and an external definition (in another translation unit). In either case, there do not appear to be any prohibitions on declaring a function both with and without inline
; there are merely implications to doing so, notably that declaring an external function both with and without inline
means the definition in that translation unit is not an inline definition.
I'm pretty sure this is valid C.
The following standard references are from C99, but the C17 draft contains exactly the same text.
6.2.2 (Linkages of identifiers) says:
For an identifier declared with the storage-class specifier
extern
in a scope in which a prior declaration of that identifier is visible, if the prior declaration specifies internal or external linkage, the linkage of the identifier at the later declaration is the same as the linkage specified at the prior declaration. If no prior declaration is visible, or if the prior declaration specifies no linkage, then the identifier has external linkage.If the declaration of an identifier for a function has no storage-class specifier, its linkage is determined exactly as if it were declared with the storage-class specifier
extern
.
Thus: extern
doesn't always mean external linkage. If there is an existing declaration with internal linkage (static
) visible in the current scope, that's what takes precedence.
However, 6.11 (Future language directions) also says:
6.11.2 Linkages of identifiers
- Declaring an identifier with internal linkage at file scope without the
static
storage-class specifier is an obsolescent feature.
So while this is a valid feature of C, you probably shouldn't rely on it in new code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With