📄 ch13.htm
字号:
simultaneously.</p><p>The easiest way to ensure compatibility between code modules that are written in C and C++ is to adhere to the common denominator of these languages. Then again, using C++ as a procedural language ("better C") isn't worth the bother -- you can simply stick to C. Combining object-oriented C++ code with procedural C code into a seamless executable is more challenging -- but it offers many advantages.</p><h2> <a name="Heading24">C and C++ Linkage Conventions</a></h2><p>By default, C++ functions have C++ linkage, which is incompatible with C linkage. Consequently, global C++ functions cannot be called from C code unless they are explicitly declared as having a C linkage.</p><h3> <a name="Heading25">Forcing C Linkage on A C++ Function</a></h3><p>To override the default C++ linkage, a C++ function has to be declared <tt>extern "C"</tt>.For example</p><pre><tt>// filename decl.hpp</tt><tt><b>extern "C"</b> void f(int n); //force C linkage so that f() can be called from C</tt><tt> // code although it is compiled by a C++ compiler</tt><tt> // decl.hpp</tt></pre><p>The <tt>extern "C"</tt> prefix instructs a C++ compiler to apply C linkage to the function <tt>f()</tt> rather than the default C++ linkage. This means that a C++ compiler does not apply <i>name mangling</i> to <tt>f()</tt>, either (see the following sidebar, "What's in Name Mangling?"). Consequently, a call to <tt>f()</tt> from C code is properly resolved by a C linker. A C++ linker can also locate the compiled version of <tt>f()</tt> even though it has a C linkage type. In other words, declaring C++ functions as <tt>extern "C"</tt> guarantees interoperability between C++ and C (as well as other procedural languages that use the C calling convention). However, forcing C linkage has a price: It is impossible to overload another version of <tt>f()</tt> that is also declared as <tt>extern "C"</tt>. For example</p><pre><tt>// filename decl.hpp</tt><tt>extern "C" void f(int n);</tt><tt>extern "C" void f(float f); //error, second C linkage of f is illegal</tt><tt>// decl.hpp</tt></pre><p>Note that you can declare additional overloaded versions of <tt>f()</tt> as long as they are not declared <tt>extern "C"</tt>:</p><pre><tt>// filename decl.hpp</tt><tt>extern "C" void f(int n); //OK, can be called from C and C++ code</tt><tt>void f(float f); //OK, no C linkage used. Can be called only from C++ code</tt><tt>void f(char c); //OK, no C linkage used. Can be called only from C++ code</tt><tt>// decl.hpp</tt></pre><p>How does it work? A call to the function from C code is translated to a <cite>CALL</cite> assembly directive, followed by the function name. Declaring a C++ function as <tt>extern "C"</tt> ensures that the name that is generated for it by a C++ compiler is identical to the name that a C compiler expects. On the other hand, if the called function is compiled by a C++ compiler without the <tt>extern "C"</tt> specifier, it has a mangled name but a C compiler still places the nonmangled name after the <tt>CALL</tt> directive, resulting in a link-time error.</p><blockquote> <hr> <b>What's in Name Mangling?<br> </b>Name mangling (the more politically correct term, although rarely used, is <i>name decoration</i>) is a method used by a C++ compiler to generate unique names for identifiers in a program. The exact details of the algorithm are compiler-dependent, and they might vary from one version to another. Name mangling ensures that entities with seemingly identical names get unique identifications. The resultant mangled name contains all the information that might be needed by the linker, including linkage type, scope, calling convention, and so on. For instance, when a global function is overloaded, the generated mangled name for each overloaded version is unique. Name mangling is also applied to variables. Thus, a local variable and a global variable with the same user-given name still get distinct mangled names. How is the mangled name synthesized? The compiler picks the user-given name of an identifier and decorates it with additional affixes to indicate a variable of a fundamental type, a class, or a function. For a function, the mangled name embeds its scope and linkage type, the namespace in which it is declared, the list of parameters, the parameters' passing mechanism, and the parameters' cv-qualifications. A mangled name of a member function incorporates additional information such as the class name, whether it is a <tt>const</tt> member function, and other implementation-dependent details that the linker and the runtime environment might need. Following is an example: For a global function <tt>void func(int);</tt>, a given compiler can generate the corresponding mangled name <tt>__x_func@i@</tt>, where the affix <tt>x</tt> indicates a function, <tt>func</tt> is the function's user-given name, <tt>@</tt> indicates the beginning of the parameter list, <tt>i</tt> indicates the type of the parameter, and the closing <tt>@</tt> sign signals the end of the parameter list. An overloaded version of <tt>f()</tt> has a different mangled name because it has a different parameter list. The original user-given name can be reproduced from the mangled name, so linkers in general can issue error messages in a human-readable format. <br> <br> As was previously stated, the name mangling scheme of a given compiler can change from one version to another (for example, if the new version supports namespaces, whereas the previous one did not). This is one of the reasons you often have to recompile your code with every compiler upgrade. Another important implication is that, usually, the linker and the compiler need to come from the same vendor and have compatible versions. This ensures that they share the same naming conventions and that they produce compatible binary code. <hr></blockquote><h3> <a name="Heading26">Calling C++ Code from C Code</a></h3><p>Up until now, you have observed the C++ side of the story. A C program cannot <tt>#include</tt> the header file <tt>decl.hpp</tt> because the <tt>extern "C"</tt> specifier is not recognized by a C compiler. To ensure that the declaration can be parsed by a C compiler, <tt>extern "C"</tt> needs to be visible to a C++ compiler -- but not to a C compiler. A C++ function with C linkage has to be declared in two distinct forms, one for C++ and another for C. This can be achieved by using separate C and C++ header files. The C header file looks similar to the following:</p><pre><tt>/*** filename decl.h ***/</tt><tt>void f(int n); /* identical to the C++ header but no extern "C" here */</tt><tt> /*** decl.h ***/</tt></pre><p>The header file can be <tt>#included</tt> in the C source file that calls the function <tt>f()</tt>. For example</p><pre><tt>/*** filename do_something.c ***/</tt><tt>#include "decl.h"</tt><tt>void do_something()</tt><tt>{</tt><tt> f(5);</tt><tt>}</tt><tt>/*** do_something.c ***/</tt></pre><p>Keeping separate header files for C and C++ is not an elegant solution, however. The header files have to remain in sync all the time, and when many header files are used, this can turn into a serious maintenance problem. A better alternative is to use one or more C header files for the declarations. For example</p><pre><tt>/*** filename f.h ***/</tt><tt>void f(int n); /* identical to the C++ header but no extern "C" here */</tt><tt> /*** f.h ***/</tt><tt>/*** filename g.h ***/</tt><tt>void g(const char * pc, int n); </tt><tt> /*** g.h ***/</tt></pre><p>Next, the C header files are <tt>#included</tt> in a C++ header file that contains an <tt>extern "C"</tt> block:</p><pre><tt>// filename decl.hpp</tt><tt>extern "C"</tt><tt>{</tt><tt>#include "f.h"</tt><tt>#include "g.h"</tt><tt>}</tt><tt>// filename decl.hpp</tt></pre><p>The effect of an <tt>extern "C"</tt> block is as if every declaration in the <tt>#included</tt> header files had a preceding <tt>extern "C"</tt> specifier. Another alternative is to modify the C header file directly by adding an <tt>#ifdef</tt> directive to make the <tt>extern "C"</tt> declaration visible only to a C++ compiler. For example</p><pre><tt>/*** filename decl.h ***/</tt><tt>#ifdef __cplusplus</tt><tt>extern "C" { //visible only to a C++ compiler</tt><tt>#endif</tt><tt>void g(const char * pc, int n);</tt><tt>void f(int n);</tt><tt>#ifdef __cplusplus</tt><tt>} //visible only to a C++ compiler</tt><tt>#endif</tt><tt> /*** g.h ***/</tt></pre><p>This way, only one header file is needed. However, it is not always possible to modify the C header files directly. In such cases, the preceding technique needs to be used. Please note that a C++ function called from C code is an ordinary C++ function. It can instantiate objects, invoke their member functions, or use any other C++ feature. However, some implementations might require special configuration settings to ensure that the linker has access to the C++ libraries and template codes.</p><h3> <a name="Heading27">Compiling main()</a></h3><p>Functions can be compiled by either a C compiler or a C++ compiler. However, a C++ compiler should compile <tt>main()</tt>. This enables a C++ compiler to take care of templates, static initialization, and additional implementation-dependent operations for which <tt>main()</tt> is responsible. Compiling <tt>main()</tt> under a C compiler will most likely result in link-time errors due to the different semantics of <tt>main()</tt> in C and C++.</p><h2> <a name="Heading28">Minimize the Interface Between C and C++ Code</a></h2><p>In general, you can call a C function from C++ code without special adjustments. The opposite, as you have seen, is also possible -- but it requires additional adjustments. It is therefore recommended that you keep the interface between the two languages at a minimum. Declaring every C++ function as <tt>extern "C"</tt>, for example, is not recommended. Not only does this convention imply additional modifications to the header files, it also disables overloading. Remember also that you cannot declare a member function <tt>extern "C".</tt> For C++ functions that have to be called from C code, it might be advantageous to use a function wrapper that has an <tt>extern "C"</tt> specifier. In this case, the wrapped C++ functions can have the C++ linkage. For example</p><pre><tt>void g(const char * pc, int n); //extern "C" is unnecessary</tt><tt>void f(int n);</tt><tt>extern "C" void f_Wrapper(int n) //only the wrapper function is called from C</tt><tt>{</tt><tt> f(n);</tt><tt>}</tt><tt>extern "C" void g_Wrapper(const char *pc, int n)</tt><tt>{</tt><tt> g(pc, n);</tt><tt>}</tt></pre><h2> <a name="Heading29">Mixing <iostream> Classes with <stdio.h> Functions</a></h2><p>It is possible to use both <tt><iostream></tt> classes and <tt><stdio.h></tt> library functions in the same program, as long as they do not access the same file. For example, you can use the <tt><iostream></tt> object <tt>cin</tt> to read data from the keyboard, and then use <tt><stdio.h></tt> functions to write the data to a disk file, as in the following program:</p><pre><tt>#include <iostream></tt><tt>#include <cstdio></tt><tt>using namespace std;</tt><tt>int main()</tt><tt>{</tt><tt> int num;</tt><tt> cin>>num;</tt><tt> cout<<"you enetred: "<< num <<endl;</tt><tt> FILE *fout = fopen("data.dat", "w");</tt><tt> if (fout) //write num to a disk file</tt><tt> {</tt><tt> fprintf(fout, "%d\n", num);</tt><tt> }</tt><tt> fclose(fout);</tt><tt> return 0;</tt><tt>}</tt></pre><p>It is even possible to use <tt><iostream></tt> and <tt><stdio.h></tt> to manipulate the same file; for instance, a program can send output to both <tt>stdout</tt> and <tt>cout</tt>, although this is not recommended. To enable simultaneous access to the same file, you first have to call <tt>ios::sync_with_stdio(true);</tt> to synchronize the I/O operations. Note, however, that this synchronization degrades performance. Therefore, only use it when <tt><iostream></tt> and <tt><stdio.h></tt> access the same file. For example</p><pre><tt>#include <iostream></tt><tt>#include <cstdio></tt><tt>using namespace std;</tt><tt>int main()</tt><tt>{</tt><tt> ios::sync_with_stdio(true);//enable mixed I/O</tt><tt> int num;</tt><tt> printf("please enter a number\n");</tt><tt> cin>>num;</tt><tt> cout<<"you enetred: "<< num << "please enter another one " << endl;</tt><tt> scanf("%d", &num);</tt><tt> return 0;</tt><tt>}</tt></pre><p>Normally, you won't write such code. However, when a large application combines legacy C functions that use <tt><stdio.h></tt> and C++ objects that use <tt><iostream></tt>, I/O synchronization is unavoidable because, ultimately, the same low-level system resources are used by both <tt><stdio.h></tt> and <tt><iostream></tt>.</p><p>The fact that <tt><iostream></tt> and <tt><stdio.h></tt> can be combined is a major advantage. Otherwise, the migration process from C to C++ might be much fussier, and making C and C++ code work together might prove to be very difficult.</p><h2> <a name="Heading30">Accessing a C++ Object in C Code</a></h2><p>Can C code, which of course is unaware of object semantics, access the data members of a C++ object directly? The short answer is, "Yes, but". There are some guarantees about the underlying memory layout of an object; C code can take advantage of these guarantees and treat a C++ object as an ordinary data struct, provided that all the following restrictions apply to the class of the object in question:</p><ul> <li> <p> The class has no virtual member functions (including inherited virtual functions of a base class).</p> </li> <p></p> <li> <p> The class has no virtual base classes in the entire inheritance chain.</p> </li> <p></p> <li> <p> The class has no member objects that have either a virtual base class or virtual member functions.</p> </li> <p></p> <li> <p> All the data members of the class are declared without an intervening access specifier. </p> </li></ul><p></p><h3> <a name="Heading31">The Underlying Representation of an Object in Memory</a></h3><p>Examine these restrictions in more detail, given the following declaration of the class <tt>Date</tt>:</p><pre><tt>class Date</tt><tt>{</tt><tt>public:</tt><tt> int day;</tt><tt> int month;</tt><tt> int year;</tt><tt> //constructor and destructor</tt><tt> Date(); //current date</tt>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -