The introduction

In the source code of a project using C++, it is often unavoidable to see the following code:

#ifdef __cplusplus extern "C" { #endif /*... */ #ifdef __cplusplus } #endifCopy the code

What does it do, you know? And such questions often come up in interviews or written tests. I will introduce it from the following aspects:

  • #ifdef _cplusplus/#endif _cplusplus
  • 2, extern “C”
    • 2.1 extern keyword
    • 2.2, “C”
    • Extern “C”
  • C and C++ call each other
    • 3.1 C++ compilation and connection
    • 3.2 compilation and connection of C
    • 3.3 C++ call C code
    • 3.4 C call C++ code
  • C and C++ mix calls to special function Pointers

#ifdef _cplusplus/#endif _cplusplus

Extern “C” before introducing extern “C”, let’s take a look at what #ifdef _cplusplus/#endif _cplusplus does. #ifdef/#endif, #ifndef/#endif for conditional compilation, #ifdef _cplusplus/#endif _cplusplus — if the macro _cplusplus is defined, the statement between #ifdef/#endif is executed. Otherwise, it will not be executed.

Why #ifdef _cplusplus/#endif _cplusplus? Extern “C” declarations are not supported in C. Extern “C” declarations are not necessary in C if you understand what extern “C” does. That’s what conditional compilation does! A compile-time error occurs when extern “c” is included in a.c file.

While we’re on the subject of conditional compilation, I’ll introduce you to one of its most important applications — avoiding double inclusion of header files. I still remember that Tencent wrote a written test on this topic and gave a code similar to the following (the following is a code in the header file Mongoose. H of Mongoose, an open source Web server I have been studying recently) :

#ifndef MONGOOSE_HEADER_INCLUDED #define MONGOOSE_HEADER_INCLUDED #ifdef __cplusplus extern "C" { #endif /* __cplusplus * / / *... * do something here *................................. */ #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* MONGOOSE_HEADER_INCLUDED */Copy the code

Then ask you to explain the above macro #ifndef/#endif function? To explain a problem, let’s look at two facts:

  • This header file, mongoose.h, may be included by multiple source files in a project (#include “mongoose.h”), and for a large project, this redundancy may cause errors because a header file contains a class definition or inline function, Mongoose. h may be #include twice in one source file (e.g., the A.h header contains mongoose.h, and #include a.h and mongoose.h in the B.c file) – this will cause an error (a structure, class, etc is defined twice in the same source file).
  • Removing these redundancies is required both from a logical point of view and to reduce compile time. However, it is not only tedious and impractical for programmers to analyze and remove these redundancies, but most importantly, it is sometimes needed to keep modules independent.

To solve this problem, in the code above

#ifndef MONGOOSE_HEADER_INCLUDED #define MONGOOSE_HEADER_INCLUDED /*…………………………… */ #endif /* MONGOOSE_HEADER_INCLUDED */

It works. If MONGOOSE_HEADER_INCLUDED, the contents between #ifndef/#endif are ignored. Therefore, when the MONGOOSE_HEADER_INCLUDED value is given, the contents of the MONGOOSE_HEADER_INCLUDED header file are read for the first time at compile time. If the MONGOOSE_HEADER_INCLUDED header file is not included, MONGOOSE_HEADER_INCLUDED will not be included.

2, extern “C”

Extern “C” starts with a literal analysis of extern “C”, which consists of two parts — the extern keyword, “C”. Extern “C” and extern “C”

2.1 extern keyword

Functions, variables, enumerations, etc. must be consistent across all source files in a project unless you specify local. Let’s start with an example:

//file1.c: int x=1; int f(){do something here} //file2.c: extern int x; int f(); void g(){x=f(); }Copy the code

The x and f() used by g() in file2.c are defined in file1.c. Extern specifies that x in file2. C is only a declaration of a variable. It does not define x and does not allocate memory for x. The variable X can be defined as a global variable only once in all modules, otherwise a connection error will occur. However, the declaration can be multiple times and the declaration must be of the same type. For example:

//file1.c: int x=1; int b=1; extern c; //file2.c: int x; // x equals to default of int type 0 int f(); extern double b; extern int c;Copy the code

There are three errors in this code:

  1. X is defined twice
  2. B is declared twice as a different type
  3. C is declared twice, but it’s not defined

Returning to the extern keyword, extern is the C/C++ keyword that indicates the scope (visibility) of functions and global variables. This keyword tells the compiler that its declared functions and variables can be used in this module or in other modules. Normally, functions and global variables that this module provides references to other modules are declared with the extern keyword in a module’s header file. For example, if module B wants to reference global variables and functions defined in module A, it only needs to include module A’s header file. In this way, when module B calls the function in module A, module B will not report an error although it cannot find the function at the compilation stage. It will find this function in the connection phase from the object code generated by the compilation of module A.

The extern keyword is static, and global variables and functions that it modifies are used only in this module. Thus, a function or variable cannot be extern “C” qualified if it is only possible to be used by this module.

2.2, “C”

Typically, a C++ program contains parts of code written in other languages. Similarly, snippets of code written in C++ may be used in code written in other languages. It is difficult for code written in different languages to call each other, even code written in the same language but compiled by different compilers. For example, different languages and different implementations of the same language may differ in keeping parameters and their layout on the stack in registered variables.

To make them follow uniform rules, extern can be used to specify a compile and join specification. For example, declare the C and C++ standard library function strcyp() and specify that it should be linked according to C’s compile and join specifications:

extern "C" char* strcpy(char*,const char*);Copy the code

Notice how it differs from the following declaration:

extern char* strcpy(char*,const char*);Copy the code

The following declaration simply means that strcpy() is called at connection time.

The extern “C” directive is useful because C is closely related to C++. Note: the C in extern “C” directives stands for a compile and join protocol, not a language. C stands for any language that complies with C’s compilation and join specifications, such as Fortran, Assembler, and so on.

It should also be noted that the extern “C” directive only specifies compilation and join specifications, but does not affect semantics. For example, if you specify extern “C” in a function declaration, you still have to follow the C++ rules of type detection and parameter conversion.

To declare a variable instead of defining one, you must specify the extern keyword when declaring it, but when you add “C” to it, it doesn’t change the semantics, but it does change how it is compiled and joined.

Extern “C”{} Extern “C”{} extern “C”{} extern “C”{} extern “C”{} extern “C”{}

Extern “C”

From the analysis in the previous two sections, we know that extern “C” is really for c-like and C++ hybrid programming. Extern “C” prefixes a statement in a C++ source file to indicate that it compels and joins according to the compile and join specifications of class C, not the C++ compiled and join specifications. Such C – like code can call C++ functions or variables, etc. (Note: When I say class C, I mean all languages that compile and connect in the same way as C.)

C and C++ call each other

We now know that extern “C” is implemented as a mix of c-like and C++ programming. Below we respectively introduce how to call C code in C++, C call C++ code. First, understand that C and C++ call each other. You need to know the compilation and wiring differences between them, and how to use extern “C” to call each other.

3.1 C++ compilation and connection

C++ is an object-oriented language (though not a pure object-oriented language) that supports function overloading, which is a great convenience. To support this feature of function overloading, the C++ compiler actually overloads the following functions:

void print(int i);
void print(char c);
void print(float f);
void print(char* s);Copy the code

Compile as follows:

_print_int
_print_char
_print_float
_pirnt_stringCopy the code

Such a function name to uniquely identify each function. Note: Different compiler implementations may differ, but all use this mechanism. So when the connection calls print(3), it looks for something like _print_int(3). As an aside, because of this, overloading is not considered polymorphic. Polymorphism is dynamically bound at run time (” multiple implementations of one interface “), and if overloading is considered polymorphic, it is at best “polymorphic” at compile time.

In C++, variables are compiled similarly, such as global variables may compile g_xx, class variables may compile c_xx, etc. This mechanism is used to find the corresponding variable.

3.2 compilation and connection of C

C doesn’t have overloads and classes, so instead of printing (int I), which is compiled to _print_int like C++, print(int I) is compiled to _print and so on. So if you call C directly from C++, it will fail because the connection is to print(3) in C, and it will look for _print_int(3). So extern “C” comes into play.

3.3 C++ call C code

Suppose a C header file cheader. h contains a function print(int I). In order to be able to call it in C++, the extern keyword must be added (for reasons described in the extern keyword section). It looks like this:

#ifndef C_HEADER
#define C_HEADER

extern void print(int i);

#endif C_HEADERCopy the code

The corresponding implementation file is cheader.c and the code is:

#include <stdio.h>
#include "cHeader.h"
void print(int i)
{
    printf("cHeader %d\n",i);
}Copy the code

The C++ code file C++. CPP references the C print(int I) function:

extern "C"{
#include "cHeader.h"
}

int main(int argc,char** argv)
{
    print(3);
    return 0;
}Copy the code

Execution program output:

3.4 C call C++ code

Now you have code that calls C++ in C, which is different from code that calls C in C++. The following code is defined in the cppheader.h header:

#ifndef CPP_HEADER
#define CPP_HEADER

extern "C" void print(int i);

#endif CPP_HEADERCopy the code

The corresponding implementation file cppheader. CPP contains the following code:

#include "cppHeader.h"

#include <iostream>
using namespace std;
void print(int i)
{
    cout<<"cppHeader "<<i<<endl;
}Copy the code

Call print in C’s code file c.c:

extern void print(int i);
int main(int argc,char** argv)
{
    print(3);
    return 0;
}Copy the code

#include “cppheader. h” header in C Extern int print(int I) will also compile an error.

C and C++ mix calls to special function Pointers

When we mix C and C++ programming, we sometimes define a function pointer in one language, and in the application we point to a function defined in the other language. This is ok if C and C++ share the same compile and join and function call mechanisms. However, such a general mechanism is not usually assumed to exist, so we must be careful to ensure that functions are called the way we expect them to be.

And when you specify how a function pointer is compiled and concatenated, all types of the function, including the function name and variables introduced by the function, are compiled and concatenated as specified. The following cases:

typedef int (*FT) (const void* ,const void*); //style of C++ extern "C"{ typedef int (*CFT) (const void*,const void*); //style of C void qsort(void* p,size_t n,size_t sz,CFT cmp); //style of C } void isort(void* p,size_t n,size_t sz,FT cmp); //style of C++ void xsort(void* p,size_t n,size_t sz,CFT cmp); //style of C //style of C extern "C" void ysort(void* p,size_t n,size_t sz,FT cmp); int compare(const void*,const void*); //style of C++ extern "C" ccomp(const void*,const void*); //style of C void f(char* v,int sz) { //error,as qsort is style of C //but compare is style of C++ qsort(v,sz,1,&compare); qsort(v,sz,1,&ccomp); //ok isort(v,sz,1,&compare); //ok //error,as isort is style of C++ //but ccomp is style of C isort(v,sz,1,&ccopm); }Copy the code

Typedefs int (*FT) (const void*,const void*) The return value is int and takes two arguments, which can be Pointers of any type (since void*).

The most typical example of an alias for a function pointer is signal, which is defined as follows:

typedef void (*HANDLER)(int);
HANDLER signal(int ,HANDLER);Copy the code

The above code defines the letter HANDLER function signal, which returns a value of type HANDLER and takes two arguments, int and HANDLER. This avoids the need to define signal functions as follows:

void (*signal (int ,void(*)(int) ))(int)Copy the code


Author: Tyler Source: http://www.cnblogs.com/skynet/ based on attribution 2.5 China mainland license agreement release, reproduction, or used for commercial purposes, but you must keep a signed this article Tyler (include link).