การใส่extern "C"
รหัส C ++ ลงไปทำอะไรกันแน่?
ตัวอย่างเช่น:
extern "C" {
void foo();
}
foo()
ฟังก์ชั่นนั้น
การใส่extern "C"
รหัส C ++ ลงไปทำอะไรกันแน่?
ตัวอย่างเช่น:
extern "C" {
void foo();
}
foo()
ฟังก์ชั่นนั้น
คำตอบ:
extern "C" ทำให้ฟังก์ชั่นชื่อใน C ++ มีการเชื่อมโยง 'C' (คอมไพเลอร์ไม่ได้ตัดชื่อ) เพื่อให้ลูกค้ารหัส C สามารถเชื่อมโยงไปยัง (เช่นใช้) ฟังก์ชั่นของคุณโดยใช้ไฟล์ส่วนหัวที่เข้ากันได้ 'C' ประกาศฟังก์ชั่นของคุณ นิยามฟังก์ชันของคุณมีอยู่ในรูปแบบไบนารี (ที่รวบรวมโดยคอมไพเลอร์ C ++ ของคุณ) ที่ตัวเชื่อมโยง 'C' จะเชื่อมโยงกับการใช้ชื่อ 'C'
เนื่องจาก C ++ มีชื่อฟังก์ชั่นมากเกินไปและ C ไม่ได้คอมไพเลอร์ C ++ จึงไม่สามารถใช้ชื่อฟังก์ชั่นเป็นรหัสเฉพาะที่จะเชื่อมโยงไปได้ดังนั้นมันจึงรวมชื่อด้วยการเพิ่มข้อมูลเกี่ยวกับอาร์กิวเมนต์ คอมไพเลอร์ AC ไม่จำเป็นต้องทำการรวมชื่อเนื่องจากคุณไม่สามารถโหลดชื่อฟังก์ชันใน C ได้มากเกินไปเมื่อคุณระบุว่าฟังก์ชันมีลิงก์ภายนอก "C" ใน C ++ คอมไพเลอร์ C ++ จะไม่เพิ่มข้อมูลประเภทอาร์กิวเมนต์ / พารามิเตอร์ให้กับชื่อที่ใช้ การเชื่อมต่อ
เพื่อให้คุณรู้ว่าคุณสามารถระบุ "C" การเชื่อมโยงไปยังแต่ละประกาศ / คำนิยามอย่างชัดเจนหรือใช้บล็อกเพื่อจัดกลุ่มลำดับของการประกาศ / คำนิยามที่จะมีการเชื่อมโยงบางอย่าง:
extern "C" void foo(int);
extern "C"
{
void g(char);
int i;
}
หากคุณใส่ใจในด้านเทคนิคพวกเขาจะถูกระบุไว้ในมาตรา 7.5 ของมาตรฐาน C ++ 03 ต่อไปนี้เป็นบทสรุปโดยย่อ (โดยเน้นที่ extern "C"):
extern "C" { int i; }
เป็นคำจำกัดความ void g(char);
นี้อาจจะไม่ใช่สิ่งที่คุณตั้งใจติดกับที่ไม่ใช่ความหมายของ extern "C" { extern int i; }
ที่จะทำให้มันไม่ใช่ความหมายที่คุณจะต้อง ในทางตรงกันข้ามไวยากรณ์ประกาศหนึ่งโดยไม่ต้องวงเล็บปีกกาทำให้การประกาศที่ไม่ใช่คำนิยาม: extern "C" int i;
เป็นเช่นเดียวกับextern "C" { extern int i; }
แค่ต้องการเพิ่มข้อมูลเล็กน้อยเนื่องจากฉันยังไม่เห็นมันโพสต์
คุณมักจะเห็นรหัสในส่วนหัวของ C เช่น:
#ifdef __cplusplus
extern "C" {
#endif
// all of your legacy C code here
#ifdef __cplusplus
}
#endif
สิ่งนี้สำเร็จคือช่วยให้คุณสามารถใช้ไฟล์ส่วนหัว C ที่มีรหัส C ++ ของคุณเพราะจะมีการกำหนดแมโคร "__cplusplus" แต่คุณยังสามารถใช้กับรหัส C เดิมได้ซึ่งไม่ได้กำหนดแมโครไว้ดังนั้นจึงไม่เห็นโครงสร้าง C ++ ที่ไม่ซ้ำกัน
แม้ว่าฉันได้เห็นรหัส C ++ เช่น:
extern "C" {
#include "legacy_C_header.h"
}
ซึ่งฉันจินตนาการถึงความสำเร็จในสิ่งเดียวกัน
ไม่แน่ใจว่าวิธีไหนดีกว่า แต่ฉันได้เห็นทั้งคู่แล้ว
extern "C"
ในส่วนหัว) มันใช้งานได้ดีใช้เทคนิคนี้หลายครั้ง
extern "C"
ก่อนหรือหลังรวมส่วนหัวด้วย เมื่อถึงคอมไพเลอร์ก็เป็นเพียงข้อความสตรีมที่ประมวลผลล่วงหน้ายาวหนึ่งรายการแล้ว
g++
ใดผิดพลาดสำหรับเป้าหมายใด ๆ ในเวลาใด ๆ ในช่วง 17 ปีที่ผ่านมาอย่างน้อยที่สุด จุดรวมของตัวอย่างแรกคือมันไม่สำคัญว่าคุณจะใช้คอมไพเลอร์ C หรือ C ++ จะไม่มีการสร้างชื่อใด ๆ สำหรับชื่อในextern "C"
บล็อก
ถอดรหัสg++
ไบนารีที่สร้างขึ้นเพื่อดูว่าเกิดอะไรขึ้น
main.cpp
void f() {}
void g();
extern "C" {
void ef() {}
void eg();
}
/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }
รวบรวมและแยกเอลฟ์เอาท์พุตที่สร้างขึ้น:
g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cpp
readelf -s main.o
ผลลัพธ์ประกอบด้วย:
8: 0000000000000000 7 FUNC GLOBAL DEFAULT 1 _Z1fv
9: 0000000000000007 7 FUNC GLOBAL DEFAULT 1 ef
10: 000000000000000e 17 FUNC GLOBAL DEFAULT 1 _Z1hv
11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _Z1gv
13: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND eg
การตีความ
เราเห็นว่า:
ef
และeg
ถูกเก็บไว้ในสัญลักษณ์ที่มีชื่อเดียวกับในรหัส
สัญลักษณ์อื่น ๆ ถูกทำให้ยุ่งเหยิง เรามาแก้ปัญหาให้พวกมัน
$ c++filt _Z1fv
f()
$ c++filt _Z1hv
h()
$ c++filt _Z1gv
g()
สรุป: ทั้งสองประเภทสัญลักษณ์ต่อไปนี้ไม่ได้ถูกทำให้ยุ่งเหยิง:
Ndx = UND
) เพื่อให้ที่ลิงก์หรือเวลารันจากไฟล์อ็อบเจ็กต์อื่นดังนั้นคุณจะต้องใช้extern "C"
ทั้งคู่เมื่อโทร:
g++
ให้คาดหวังว่าจะมีสัญลักษณ์ที่ไม่มีการแกะสลักgcc
g++
ให้สร้างสัญลักษณ์ unmangled gcc
เพื่อใช้สิ่งที่ไม่ทำงานในภายนอกค
เห็นได้ชัดว่าฟีเจอร์ C ++ ใด ๆ ที่ต้องใช้ชื่อ mangling จะไม่ทำงานภายในextern C
:
extern "C" {
// Overloading.
// error: declaration of C function ‘void f(int)’ conflicts with
void f();
void f(int i);
// Templates.
// error: template with C linkage
template <class C> void f(C i) { }
}
C ที่รันได้น้อยที่สุดจากตัวอย่าง C ++
เพื่อความสมบูรณ์และสำหรับ newbs ออกมีดูด้วย: วิธีการใช้ไฟล์ต้นฉบับ C ในโครงการ C ++?
การเรียก C จาก C ++ นั้นง่ายมาก: ฟังก์ชัน C แต่ละตัวมีสัญลักษณ์ที่ไม่เป็นไปได้เพียงสัญลักษณ์เดียวเท่านั้นดังนั้นจึงไม่จำเป็นต้องทำงานเพิ่มเติม
main.cpp
#include <cassert>
#include "c.h"
int main() {
assert(f() == 1);
}
CH
#ifndef C_H
#define C_H
/* This ifdef allows the header to be used from both C and C++
* because C does not know what this extern "C" thing is. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif
#endif
ซีซี
#include "c.h"
int f(void) { return 1; }
วิ่ง:
g++ -c -o main.o -std=c++98 main.cpp
gcc -c -o c.o -std=c89 c.c
g++ -o main.out main.o c.o
./main.out
ไม่มีextern "C"
ลิงก์ล้มเหลวด้วย:
main.cpp:6: undefined reference to `f()'
เพราะg++
คาดว่าจะได้พบกับ mangled f
ซึ่งgcc
ไม่ได้ผลิต
ตัวอย่างบน GitHub
C ++ ที่รันได้น้อยที่สุดจากตัวอย่าง C
การเรียก C ++ จาก C นั้นยากขึ้นนิดหน่อย: เราต้องสร้างแต่ละฟังก์ชั่นที่ไม่ใช่แบบที่แยกกันของแต่ละฟังก์ชั่นที่เราต้องการเปิดเผย
ที่นี่เราแสดงให้เห็นถึงวิธีการเปิดเผยฟังก์ชั่น C ++ เกินพิกัดถึง C
main.c
#include <assert.h>
#include "cpp.h"
int main(void) {
assert(f_int(1) == 2);
assert(f_float(1.0) == 3);
return 0;
}
cpp.h
#ifndef CPP_H
#define CPP_H
#ifdef __cplusplus
// C cannot see these overloaded prototypes, or else it would get confused.
int f(int i);
int f(float i);
extern "C" {
#endif
int f_int(int i);
int f_float(float i);
#ifdef __cplusplus
}
#endif
#endif
cpp.cpp
#include "cpp.h"
int f(int i) {
return i + 1;
}
int f(float i) {
return i + 2;
}
int f_int(int i) {
return f(i);
}
int f_float(float i) {
return f(i);
}
วิ่ง:
gcc -c -o main.o -std=c89 -Wextra main.c
g++ -c -o cpp.o -std=c++98 cpp.cpp
g++ -o main.out main.o cpp.o
./main.out
ไม่extern "C"
ว่าจะล้มเหลวด้วย:
main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'
เนื่องจากg++
สัญลักษณ์ที่สร้างเป็น mangled ซึ่งgcc
ไม่สามารถหาได้
ตัวอย่างบน GitHub
ทดสอบใน Ubuntu 18.04
extern "C" {
ช่วยให้คุณเรียกใช้ฟังก์ชัน C unmangled จากภายในโปรแกรม C ++เช่นเดียวกับฟังก์ชั่น C ++ ที่ไม่จำเป็นจากโปรแกรม Cซึ่งคำตอบอื่นไม่ชัดเจนและ 2) เพราะคุณแสดงตัวอย่างที่ชัดเจน แต่ละ. ขอบคุณ!
ในทุกโปรแกรม C ++ ฟังก์ชั่นที่ไม่คงที่ทั้งหมดจะถูกแสดงในไฟล์ไบนารีเป็นสัญลักษณ์ สัญลักษณ์เหล่านี้เป็นสตริงข้อความพิเศษที่ระบุฟังก์ชันในโปรแกรมโดยเฉพาะ
ใน C ชื่อสัญลักษณ์จะเหมือนกับชื่อฟังก์ชัน สิ่งนี้เป็นไปได้เพราะใน C ไม่มีฟังก์ชั่นไม่คงที่สองตัวที่มีชื่อเดียวกัน
เนื่องจาก C ++ อนุญาตให้โหลดเกินพิกัดและมีคุณสมบัติมากมายที่ C ไม่เหมือนคลาส, ฟังก์ชันสมาชิก, ข้อมูลจำเพาะข้อยกเว้น - จึงไม่สามารถใช้ชื่อฟังก์ชันเป็นชื่อสัญลักษณ์ได้ เพื่อแก้ปัญหานั้น C ++ ใช้ชื่อที่เรียกว่า mangling ซึ่งเปลี่ยนชื่อฟังก์ชั่นและข้อมูลที่จำเป็นทั้งหมด (เช่นจำนวนและขนาดของอาร์กิวเมนต์) เป็นสตริงที่ดูแปลก ๆ ซึ่งประมวลผลโดยคอมไพเลอร์และลิงเกอร์เท่านั้น
ดังนั้นหากคุณระบุฟังก์ชั่นให้เป็น extern C คอมไพเลอร์จะไม่ทำการตั้งชื่อ mangling ด้วยมันและสามารถเข้าถึงได้โดยตรงโดยใช้ชื่อสัญลักษณ์เป็นชื่อฟังก์ชั่น
สิ่งนี้มีประโยชน์ในขณะที่ใช้งานdlsym()
และdlopen()
สำหรับการเรียกใช้ฟังก์ชันดังกล่าว
ภาษาการเขียนโปรแกรมส่วนใหญ่ไม่ได้สร้างอยู่ด้านบนของภาษาโปรแกรมที่มีอยู่ C ++ ถูกสร้างขึ้นบน C และยิ่งไปกว่านั้นมันเป็นภาษาการเขียนโปรแกรมเชิงวัตถุที่สร้างขึ้นจากภาษาการเขียนโปรแกรมขั้นตอนและด้วยเหตุนี้จึงมีการแสดงออกของ C ++ เหมือนextern "C"
ที่ให้ความเข้ากันได้กับ C ย้อนหลัง
ลองดูตัวอย่างต่อไปนี้:
#include <stdio.h>
// Two functions are defined with the same name
// but have different parameters
void printMe(int a) {
printf("int: %i\n", a);
}
void printMe(char a) {
printf("char: %c\n", a);
}
int main() {
printMe("a");
printMe(1);
return 0;
}
AC คอมไพเลอร์จะไม่คอมไพล์ตัวอย่างข้างต้นเนื่องจากฟังก์ชันเดียวกันprintMe
ถูกกำหนดสองครั้ง (แม้ว่าจะมีพารามิเตอร์ที่แตกต่างกันint a
vs char a
)
gcc -o printMe printMe.c && ./printMe;
1 ข้อผิดพลาด PrintMe ถูกกำหนดมากกว่าหนึ่งครั้ง
คอมไพเลอร์ C ++ จะรวบรวมตัวอย่างข้างต้น มันไม่สนใจว่าprintMe
จะถูกกำหนดสองครั้ง
g ++ -o printMe printMe.c && ./printMe;
นี่เป็นเพราะคอมไพเลอร์ C ++ โดยนัยเปลี่ยนชื่อฟังก์ชั่น( mangles ) ขึ้นอยู่กับพารามิเตอร์ของพวกเขา ใน C คุณสมบัตินี้ไม่รองรับ อย่างไรก็ตามเมื่อ C ++ ถูกสร้างขึ้นบน C ภาษาได้รับการออกแบบให้เน้นวัตถุและจำเป็นต้องสนับสนุนความสามารถในการสร้างคลาสที่แตกต่างด้วยเมธอด (ฟังก์ชั่น) ในชื่อเดียวกันและเพื่อแทนที่เมธอด ( เมธอดแทนที่ ) พารามิเตอร์
extern "C"
พูดว่า "อย่าย่อชื่อฟังก์ชัน C"อย่างไรก็ตามลองจินตนาการว่าเรามีไฟล์ C รุ่นเก่าที่ชื่อว่า "parent.c" ซึ่งinclude
เป็นชื่อฟังก์ชั่นจากไฟล์ C รุ่นเก่าอื่น ๆ "parent.h", "child.h" ฯลฯ หากไฟล์เดิม "parent.c" ทำงาน ผ่านคอมไพเลอร์ C ++ จากนั้นชื่อฟังก์ชั่นจะถูกจัดการและพวกเขาจะไม่ตรงกับชื่อฟังก์ชั่นที่ระบุใน "parent.h", "child.h" เป็นต้น - ดังนั้นชื่อฟังก์ชันในไฟล์ภายนอกเหล่านั้นจะต้อง แหลกเหลว Mangling ชื่อฟังก์ชั่นในโปรแกรม C ที่ซับซ้อนผู้ที่มีการพึ่งพาจำนวนมากสามารถนำไปสู่รหัสที่เสีย ดังนั้นจึงสะดวกที่จะระบุคีย์เวิร์ดที่สามารถบอกคอมไพเลอร์ C ++ ว่าจะไม่ทำให้ชื่อฟังก์ชันยุ่งเหยิง
extern "C"
คำหลักที่บอกว่าเป็น C ++ คอมไพเลอร์ไม่ได้ชื่อฉีก (เปลี่ยนชื่อ) C ฟังก์ชั่น
ตัวอย่างเช่น:
extern "C" void printMe(int a);
extern "C"
ถ้าเรามีเพียงdll
ไฟล์? ฉันหมายความว่าถ้าเราไม่มีไฟล์ส่วนหัวและเพิ่งมีไฟล์ต้นฉบับ (เพิ่งใช้งาน) และการใช้ฟังก์ชั่นผ่านตัวชี้ฟังก์ชั่น ในสถานะนี้เราเพิ่งใช้ฟังก์ชั่น (โดยไม่คำนึงถึงชื่อ)
C-header ใด ๆ ไม่สามารถใช้งานร่วมกับ C ++ ได้โดยการรวมอยู่ใน "C" ภายนอก เมื่อตัวระบุใน C-header ขัดแย้งกับคำสำคัญ C ++ คอมไพเลอร์ C ++ จะบ่นเกี่ยวกับเรื่องนี้
ตัวอย่างเช่นฉันเห็นรหัสต่อไปนี้ล้มเหลวใน g ++:
extern "C" {
struct method {
int virtual;
};
}
Kinda เข้าท่า แต่เป็นสิ่งที่ควรคำนึงถึงเมื่อทำการย้าย C-code ไปที่ C ++
extern "C"
หมายถึงการใช้การเชื่อมโยง C ตามที่อธิบายโดยคำตอบอื่น ๆ ไม่ได้หมายความว่า "รวบรวมเนื้อหาเป็น C" หรืออะไรก็ตาม int virtual;
ไม่ถูกต้องใน C ++ และการระบุลิงก์ที่แตกต่างกันจะไม่เปลี่ยนแปลง
มันเปลี่ยนแปลงการเชื่อมโยงของการทำงานในลักษณะดังกล่าวว่าการทำงานเป็น callable จากซีในทางปฏิบัตินั่นหมายความว่าชื่อฟังก์ชันไม่ได้เป็นmangled
undname
ชื่อ
มันแจ้งให้คอมไพเลอร์ C ++ เพื่อค้นหาชื่อของฟังก์ชั่นเหล่านั้นในรูปแบบ C เมื่อทำการเชื่อมโยงเพราะชื่อของฟังก์ชั่นที่คอมไพล์ใน C และ C ++ จะแตกต่างกันระหว่างขั้นตอนการเชื่อมโยง
extern "C" หมายถึงการได้รับการยอมรับโดยคอมไพเลอร์ C ++ และเพื่อแจ้งให้คอมไพเลอร์ทราบว่าฟังก์ชั่นที่บันทึกไว้ (หรือจะ) คอมไพล์ในสไตล์ C ดังนั้นในขณะที่เชื่อมโยงมันจะเชื่อมโยงไปยังรุ่นที่ถูกต้องของฟังก์ชันจาก C
ฉันใช้ 'extern "C"' มาก่อนสำหรับไฟล์ dll (dynamic link library) เพื่อทำฟังก์ชั่นหลัก () ฟังก์ชั่น "exportable" ดังนั้นมันสามารถใช้งานได้ในภายหลังใน dll ที่สามารถเรียกใช้งานได้ บางทีตัวอย่างที่ฉันเคยใช้อาจมีประโยชน์
DLL
#include <string.h>
#include <windows.h>
using namespace std;
#define DLL extern "C" __declspec(dllexport)
//I defined DLL for dllexport function
DLL main ()
{
MessageBox(NULL,"Hi from DLL","DLL",MB_OK);
}
EXE
#include <string.h>
#include <windows.h>
using namespace std;
typedef LPVOID (WINAPI*Function)();//make a placeholder for function from dll
Function mainDLLFunc;//make a variable for function placeholder
int main()
{
char winDir[MAX_PATH];//will hold path of above dll
GetCurrentDirectory(sizeof(winDir),winDir);//dll is in same dir as exe
strcat(winDir,"\\exmple.dll");//concentrate dll name with path
HINSTANCE DLL = LoadLibrary(winDir);//load example dll
if(DLL==NULL)
{
FreeLibrary((HMODULE)DLL);//if load fails exit
return 0;
}
mainDLLFunc=(Function)GetProcAddress((HMODULE)DLL, "main");
//defined variable is used to assign a function from dll
//GetProcAddress is used to locate function with pre defined extern name "DLL"
//and matcing function name
if(mainDLLFunc==NULL)
{
FreeLibrary((HMODULE)DLL);//if it fails exit
return 0;
}
mainDLLFunc();//run exported function
FreeLibrary((HMODULE)DLL);
}
extern "C"
และ__declspec(dllexport)
ไม่เกี่ยวข้อง การควบคุมการตกแต่งสัญลักษณ์ในอดีตนั้นส่วนหลังมีหน้าที่สร้างรายการส่งออก คุณสามารถส่งออกสัญลักษณ์โดยใช้การตกแต่งชื่อ C ++ ได้เช่นกัน นอกเหนือจากจุดที่ขาดหายไปของคำถามนี้อย่างสมบูรณ์แล้วยังมีข้อผิดพลาดอื่น ๆ ในตัวอย่างโค้ดด้วย สำหรับหนึ่งmain
ส่งออกจาก DLL ของคุณไม่ได้ประกาศค่าตอบแทน หรือเรียกแบบแผนสำหรับเรื่องนั้น เมื่อนำเข้าคุณจะกำหนดแผนการโทรแบบสุ่ม ( WINAPI
) และใช้สัญลักษณ์ที่ไม่ถูกต้องสำหรับบิลด์ 32 บิต (ควรเป็น_main
หรือ_main@0
) ขออภัย -1
void*
แต่การใช้งานของคุณจะไม่ส่งคืนสิ่งใด มันจะบินได้ดีจริงๆ ...
extern "C"
เป็นข้อกำหนดการเชื่อมโยงที่ใช้ในการเรียกใช้ฟังก์ชัน Cในไฟล์ที่มาภ เราสามารถเรียกใช้ฟังก์ชัน C, เขียนตัวแปรและรวมส่วนหัว ฟังก์ชั่นถูกประกาศในนิติบุคคลภายนอก & มันถูกกำหนดไว้ภายนอก ไวยากรณ์คือ
ประเภท 1:
extern "language" function-prototype
ประเภทที่ 2:
extern "language"
{
function-prototype
};
เช่น:
#include<iostream>
using namespace std;
extern "C"
{
#include<stdio.h> // Include C Header
int n; // Declare a Variable
void func(int,int); // Declare a function (function prototype)
}
int main()
{
func(int a, int b); // Calling function . . .
return 0;
}
// Function definition . . .
void func(int m, int n)
{
//
//
}
คำตอบนี้มีไว้สำหรับผู้ที่ใจร้อน / มีกำหนดส่งงานให้พบเพียงคำอธิบายเพียงส่วนเดียวเท่านั้น:
ดังนั้น
ใน C ++ ที่มีชื่อ mangling identities ที่ไม่ซ้ำกันแต่ละฟังก์ชัน
ใน C แม้ว่าจะไม่มีชื่อ mangling identities ที่ไม่ซ้ำกันแต่ละฟังก์ชัน
ในการเปลี่ยนพฤติกรรมของ C ++ นั่นคือเพื่อระบุว่าชื่อ mangling ไม่ควรเกิดขึ้นสำหรับฟังก์ชั่นหนึ่งคุณสามารถใช้extern "C"ก่อนชื่อฟังก์ชั่นไม่ว่าด้วยเหตุผลใดก็ตามเช่นการส่งออกฟังก์ชั่นที่มีชื่อเฉพาะจาก dll สำหรับใช้งานโดยลูกค้า
อ่านคำตอบอื่น ๆ สำหรับคำตอบที่ละเอียดมากขึ้น / ถูกต้องมากขึ้น
เมื่อผสม C และ C ++ (เช่นการเรียกใช้ฟังก์ชัน C จาก C ++; และ b. การเรียกใช้ฟังก์ชัน C ++ จาก C) ชื่อ C ++ จะทำให้เกิดปัญหาในการเชื่อมโยง ในทางเทคนิคแล้วปัญหานี้เกิดขึ้นเฉพาะเมื่อฟังก์ชั่น callee ได้รับการรวบรวมเป็นไบนารี่ (ส่วนใหญ่แล้วเป็นไฟล์ไลบรารี * .a) โดยใช้คอมไพเลอร์ที่เกี่ยวข้อง
ดังนั้นเราต้องใช้ extern "C" เพื่อปิดการใช้งานชื่อ mangling ใน C ++
โดยไม่ขัดแย้งกับคำตอบที่ดีอื่น ๆ ฉันจะเพิ่มตัวอย่างของฉันเล็กน้อย
อะไรที่คอมไพเลอร์ C ++ทำ: มันจะรวบรวมชื่อในกระบวนการรวบรวมดังนั้นเราจึงต้องการให้คอมไพเลอร์ปฏิบัติต่อ C
การนำไปใช้เป็นพิเศษ
เมื่อเราสร้างคลาส C ++ และเพิ่มextern "C"
เราจะบอกคอมไพเลอร์ C ++ ของเราว่าเรากำลังใช้หลักการเรียก C
เหตุผล (เรากำลังเรียกใช้ C จาก C ++): เราต้องการเรียกใช้ฟังก์ชัน C จาก C ++ หรือเรียกใช้ฟังก์ชัน C ++ จาก C (C ++ คลาส ... ฯลฯ ไม่ทำงานใน C)
ฟังก์ชั่นเป็นโมฆะ f () รวบรวมโดยคอมไพเลอร์ C และฟังก์ชั่นที่มีชื่อเดียวกันเป็นโมฆะ f () รวบรวมโดยคอมไพเลอร์ C ++ ไม่ได้เป็นฟังก์ชั่นเดียวกัน หากคุณเขียนฟังก์ชันนั้นใน C แล้วลองโทรจาก C ++ ตัวลิงก์จะมองหาฟังก์ชัน C ++ และไม่พบฟังก์ชัน C
extern "C" บอกคอมไพเลอร์ C ++ ว่าคุณมีฟังก์ชั่นที่คอมไพล์โดยคอมไพเลอร์ C เมื่อคุณบอกว่ามันถูกคอมไพล์โดยคอมไพเลอร์ C คอมไพเลอร์ C ++ จะรู้วิธีเรียกมันอย่างถูกต้อง
นอกจากนี้ยังอนุญาตให้คอมไพเลอร์ C ++ สามารถรวบรวมฟังก์ชัน C ++ ในลักษณะที่คอมไพเลอร์ C สามารถเรียกใช้ได้ ฟังก์ชันนั้นจะเป็นฟังก์ชัน C อย่างเป็นทางการ แต่เนื่องจากคอมไพเลอร์รวบรวมโดย C ++ จึงสามารถใช้คุณลักษณะ C ++ ทั้งหมดและมีคำหลัก C ++ ทั้งหมด
extern "C"
ฟังก์ชั่น - และ (ภายใต้ข้อ จำกัด บางอย่าง) มันจะสามารถเรียกใช้งานได้โดยโค้ดที่คอมไพล์โดยคอมไพเลอร์ C