简体   繁体   中英

External linkage and »extern “C”« block

I have an int ID , which I want to define in C++ and make available for C linkage (contrived case for the sake of simplicity):

/* i.h */
#ifdef __cplusplus
extern "C" {
#endif
        extern int ID;
#ifdef __cplusplus
}
#endif

Here's a C and a C++ program using the int :

/* m.cpp */
#include "i.h"
#include <iostream>
int main() { std::cout << ID << std::endl; }

/* m.c */
#include "i.h"
#include <stdio.h>
int main() { printf("%d\n", ID); }

Now what I'm wondering is the syntax of extern "C" and/or extern . Here's how int ID can and cannot be defined:

/* i.cpp */
//                     const int ID = 88;   // no C linkage, obviously, LNK2019/1120
// extern "C"          const int ID = 88;   // provides C linkage
// extern "C" {        const int ID = 88; } // no C linkage, surprisingly, LNK2019/1120
// extern "C" { extern const int ID = 88; } // C linkage restored

Compiling:

cl /nologo /W4 m.cpp i.cpp /MD /EHsc
cl /nologo /W4 m.c   i.cpp /MD

What I don't understand is the syntax when extern "C" is used with a {block} . I have to repeat the extern , whereas with the blockless form of extern "C" I do not. Is this just a syntax quirk or is there more to it?

I stumbled upon this issue on page 98 of Inside COM by Dale Rogerson. There is a code listing with the nested extern and a comment intended to clarify (but which I don't understand):

#include <objbase.h>
extern "C"{
  extern const IID IID_IX = {0x32bb8320, 0x41b, 0x11cf, ...};
  // The extern is required to allocate memory for C++ constants.
}

Can anyone explain the internal extern ?

The inner extern means what expected: "the symbol is defined somewhere else, it has external linkage , don't allocate space for it in this unit (unless defined here as well)".

The outer extern "C" { ... } has maybe a bit misleading syntax, it only tells the compiler "if you are creating names of any symbols with external linkage inside this block, use traditional C naming ( C language linkage ) instead of C++ name mangling ( C++ language linkage ) for them". But it does not specify that all things inside the block are "defined somewhere else" (have external linkage ) -- they are still declared with the default internal linkage . That's why you need the inner extern as well.

The one-line variant extern "C" <variable declaration> is just a shorthand, as you probably want to define an external variable if you care about its cross-unit symbol name.

(As a side-note, I would include ih also in i.cpp , that way I wouldn't have to remember to mess with extern "C" any more in the implementation.)

The problem is that const and extern are warring with each other. const means (among other things), "Make this definition internal, unless there's an explicit extern on it.". When the definition is in an extern "C" block, there's no extern directly on the definition, so the "implicit internal" from the const takes precedence. The other definitions all have an extern directly on the definition, so it becomes external.

Here's how int ID can and cannot be defined:

Careful! extern "C" has different effects in different contexts.

N3797 §7.5/7:

A declaration directly contained in a linkage-specification is treated as if it contains the extern specifier (7.1.1) for the purpose of determining the linkage of the declared name and whether it is a definition . Such a declaration shall not specify a storage class.

 extern "C" int i; // declaration extern "C" { int i; // definition } 

So the second line in your snippet is just a declaration whilst your third line is also a definition. This changes what effect the linkage-specification has on the declarations - you can't give a name defined in a C++ translation unit C linkage. You have to make it a pure declaration.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM