Search code examples
clanguage-lawyerlinkagec17

C function declaration linkage


I'm a bit confused with a topic in the C17 standard.

In 6.2.2, point 5 you can read:

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. [...]

Meaning that it could have either internal or external linkage, depending on other declarations (if any) of that function before that one.

On the other hand, in 6.7.6.3, example 1 (points 16 and 17), you can read:

int f(void), *fip(), (*pfi)();

[...]

If the declaration occurs outside of any function, the identifiers have file scope and external linkage. [...]

So, it's ok with pfi (it's not a function, but a pointer), but what happens then to f and fip? Isn't that a contradiction? In 6.2.2, it's "as if the extern storage-class specifier was present" (which doesn't always mean it will have external linkage), but in 6.7.6.3 it seems the external linkage is given for granted.

What am I missing?

Edit: to be more specific, if we have this code in file scope:

// One random "previous declaration":
static int f(void);  // declares internal linkage
// Now, the important line, from the initial example:
int f(void);  // Internal linkage? External linkage?

6.2.2 states that the second declaration has internal linkage.

6.7.6.3 states that the second declaration has external linkage.


Solution

  • Either the example code in C 2018 6.7.6.3 16 was intended to be stand-alone code with no prior declarations or the authors of the text in C 2018 6.7.6.3 17 that says “If the declaration occurs outside of any function, the identifiers have file scope and external linkage” overlooked the fact that f could have internal linkage due to a prior declaration with static and that this new declaration would retain that prior linkage.

    As noted in the question, C 2018 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…” Not noted in the question, 6.2.2 4 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…” And of course we know 6.2.2 3 tells us “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.”

    Thus, if the example code int f(void), *fip(), (*pfi)(); in a scope in which a prior file-scope static int f(void); is visible, then the f in the example code has internal linkage.

    So C 2018 6.7.6.3 17 is wrong to say f necessarily has external linkage unless the example is intended to be stand-alone code with no prior declarations. Further, 6.7.6.3 17 is example text, and “In ISO standards, examples are without exception non-normative.” The other text quoted above is normative text, so it is binding and the example text in 6.7.6.3 17 is not.