2

I am very curious about what happens during linking, and, during my research in this area I have stabbed upon this code

#ifdef __cplusplus
extern “C” { 
#endif

extern double reciprocal (int i);

#ifdef __cplusplus
}
#endif

The code was in some header file, which was include by .c and .cpp source files of one program. It's a declaration of a function, which is then defined in .cpp file. Why does it work? I mean, during the compilation of the .cpp file this will turn into

extern "C" {
    extern double reciprocal (int i);
}

The outer extern both makes the function visible in the global scope and converts the C++ style of function names into C one. But there also is an inner extern. Is it OK the function is externed twice?

MinuxLint
  • 111
  • 9
  • 1
    Ignoring the pre-processing `#ifdef`s and just looking a the second snippet, the former is _language linkage_, with as you write, the main _intent_ to make the some entities therein (function types, as well as function names and variables with external linkage and variables) to C language linkage, with one main _effect_ that these entities will not have mangled names, and will moreover be placed in global namespace (since there are no namespaces in the C ABI). ... – dfrib Apr 27 '20 at 20:05
  • ... The inner `extern` is not a language linkage specifier but is a storage class specifier, and it will have no effect on the synergy between these two, as `reciprocal` already has external linkage even without `extern` (which in turn implies that it has language linkage, making it possibly to _link translation units_ from other languages). – dfrib Apr 27 '20 at 20:05

1 Answers1

4

The c++ language is allergic to adding new keywords so some get reused to mean different things. extern is one of these re-used keywords. It has 3 possible meanings:

  1. external linkage - the variable or function is defined somewhere else
  2. language linkage - the variable or function is defined in an "external" language
  3. explicit template instantiation declaration

In your case you are using 1 and 2. extern "C" declares that the code has "C" rather than the default "C++" linkage. This also implies external linkage so in pure C++ code you can just write:

extern "C" {
    double reciprocal (int i);
}

and reciprocal will be automatically be marked extern. Adding an extra extern has no effect and is required for the C version which doesn't have the extern "C" wrapper.

Note that if you are using single declaration version of extern "C" then using a second extern is not valid:

extern "C" extern double reciprocal (int i);

As the second extern is not required the correct declaration is:

extern "C" double reciprocal (int i);
Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
  • 1
    _"[...] and reciprocal will be automatically be marked `extern`"_ - I don't believe this is accurate, what would "marked `extern`" even mean in this context, as we have just established that the `extern` keywords is heavily overloaded in C++? The language linkage blocks just applies C language linkage to e.g. function names encompassed by its braces block _that have external linkage_ - it does not _automatically mark it as `extern`_. In some sense, it is, for such a name, comparable to marking the name with `extern "C"`. – dfrib Apr 27 '20 at 20:15
  • As an (contrived) exercise, we could even declare `reciprocal` as `static` after which the `extern "C"` block would have no effect on that particular function name, as it no longer has external linkage. – dfrib Apr 27 '20 at 20:18
  • @dfri yes, you don't even need to mark it as static, the `extern "C"` declaration of a function has to be the first declaration: "A function can be re-declared without a linkage specification after it was declared with a language specification, the second declaration will reuse the first language linkage. The opposite is not true: if the first declaration has no language linkage, it is assumed "C++", and redeclaring with another language is an error. ": https://godbolt.org/z/AeQUgu – Alan Birtles Apr 27 '20 at 20:24
  • My example was applied to `extern "C" { declaration-seq(optional) }`, which in itself is not a declaration, but which _applies_ language linkage. If e.g. one of the function declarations within `declaration-seq` is marked by `static`, the `extern "C" { ... }` block will not apply C language linkage to it. I believe your quite covers the redeclaration _beyond that_ of the declaration within the `extern "C" { ... }` block, or when using the at-declaration language linkage form `extern "C" declaration`. – dfrib Apr 27 '20 at 20:28
  • gcc still applies c linkage here: https://godbolt.org/z/EXcTH8 – Alan Birtles Apr 27 '20 at 20:33
  • Isn't this a bit weird? Try your example but as variation **V1)** mark the re-declaration at line `5` as `static`: this is all good. Then as variation **V2)** further remove `static` from the first declaration at line `2` - then we get a compiler error (_"'`double reciprocal(int)`' was declared '`extern`' and later '`static`'"_ - as expected by your previous message). But this kind of implies that for variation **V1)**, when we mark the first declaration as `static`, the name does not have external linkage? Which is why it is weird, imo, that it still gets "C" language linkage applies to it. – dfrib Apr 27 '20 at 20:39
  • Alas, have a look at this curiosity: I'm not familiar but it looks like `reciprocal` gets C language linkage in this GCC 9.3 example? https://godbolt.org/z/z4TBPr /// However, with clang 10, it does not: https://godbolt.org/z/83GtP1 . Clang respects `static` and does not give the symbol name C language linkage, and I think clang is right here. I believe my previous statement applies: `extern "C" { declaration-seq }` does **not** "mark declarations" in `declaration-seq` with `extern`, it _applies "C" language linkage_ to entites (e.g. a function name) that _have external linkage (already)_. – dfrib Apr 27 '20 at 20:46