Apa efek dari extern "C" di C ++?

1632

Apa yang sebenarnya dilakukan dengan memasukkan extern "C"kode C ++?

Sebagai contoh:

extern "C" {
   void foo();
}
Litherum
sumber
83
Saya ingin memperkenalkan Anda artikel ini: http://www.agner.org/optimize/calling_conventions.pdf Ini memberi tahu Anda lebih banyak tentang menelepon konvensi dan perbedaan antara kompiler.
Sam Liao
1
@Litherum Di atas kepala saya, ia memberi tahu kompiler untuk mengkompilasi lingkup kode menggunakan C, mengingat bahwa Anda memiliki kompilator silang. Juga, itu berarti bahwa Anda memiliki file Cpp di mana Anda memiliki foo()fungsi itu.
ha9u63ar
1
@ ha9u63ar Ini 'di luar kepala saya'. Seluruh sisa komentar Anda juga salah. Saya sarankan Anda menghapusnya.
TamaMcGlinn

Jawaban:

1560

extern "C" membuat nama-fungsi di C ++ memiliki tautan 'C' (kompiler tidak memotong nama) sehingga kode C klien dapat menautkan ke (yaitu menggunakan) fungsi Anda menggunakan file header kompatibel 'C' yang hanya berisi deklarasi fungsi Anda. Definisi fungsi Anda terkandung dalam format biner (yang dikompilasi oleh kompiler C ++ Anda) yang kemudian akan ditautkan oleh penghubung 'C' klien dengan menggunakan nama 'C'.

Karena C ++ memiliki overloading nama fungsi dan C tidak, kompiler C ++ tidak bisa hanya menggunakan nama fungsi sebagai id unik untuk ditautkan, jadi itu mengacaukan nama dengan menambahkan informasi tentang argumen. Kompiler AC tidak perlu memotong-motong nama karena Anda tidak dapat membebani nama fungsi dalam C. Ketika Anda menyatakan bahwa suatu fungsi memiliki hubungan "C" eksternal di C ++, kompiler C ++ tidak menambahkan informasi tipe argumen / parameter ke nama yang digunakan untuk keterkaitan.

Asal tahu saja, Anda dapat menentukan tautan "C" ke setiap pernyataan / definisi individu secara eksplisit atau menggunakan blok untuk mengelompokkan urutan deklarasi / definisi untuk memiliki tautan tertentu:

extern "C" void foo(int);
extern "C"
{
   void g(char);
   int i;
}

Jika Anda peduli tentang teknisnya, mereka terdaftar di bagian 7.5 dari standar C ++ 03, di sini adalah ringkasan singkat (dengan penekanan pada extern "C"):

  • extern "C" adalah spesifikasi-keterkaitan
  • Setiap kompiler harus menyediakan tautan "C"
  • spesifikasi tautan hanya akan muncul dalam lingkup namespace
  • semua jenis fungsi, nama fungsi dan nama variabel memiliki hubungan bahasa. Lihat Komentar Richard: Hanya nama fungsi dan nama variabel dengan hubungan eksternal yang memiliki hubungan bahasa
  • dua tipe fungsi dengan hubungan bahasa yang berbeda adalah tipe yang berbeda walaupun identik
  • linkage specs nest, bagian dalam menentukan tautan akhir
  • extern "C" diabaikan untuk anggota kelas
  • paling banyak satu fungsi dengan nama tertentu dapat memiliki tautan "C" (terlepas dari namespace)
  • extern "C" memaksa fungsi untuk memiliki hubungan eksternal (tidak dapat membuatnya statis) Lihat komentar Richard: 'static' inside 'extern "C"' valid; suatu entitas yang dideklarasikan memiliki hubungan internal, dan tidak memiliki hubungan bahasa
  • Tautan dari C ++ ke objek yang didefinisikan dalam bahasa lain dan ke objek yang didefinisikan dalam C ++ dari bahasa lain ditentukan oleh implementasi dan bergantung pada bahasa. Hanya di mana strategi tata letak objek dari dua implementasi bahasa cukup mirip, hubungan tersebut dapat dicapai
Faisal Vali
sumber
22
C compiler tidak menggunakan mangling yang tidak dilakukan oleh c ++. Jadi jika Anda ingin memanggil antarmuka ac dari program c ++, Anda harus menyatakan dengan jelas bahwa antarmuka c sebagai "extern c".
Sam Liao
59
@ Faisal: jangan mencoba menautkan kode yang dibuat dengan kompiler C ++ yang berbeda, meskipun referensi silang semuanya 'extern "C"'. Sering ada perbedaan antara tata letak kelas, atau mekanisme yang digunakan untuk menangani pengecualian, atau mekanisme yang digunakan untuk memastikan variabel diinisialisasi sebelum digunakan, atau perbedaan lainnya, plus Anda mungkin memerlukan dua perpustakaan dukungan run-time C ++ yang terpisah (satu untuk setiap kompiler).
Jonathan Leffler
8
'extern "C" memaksa fungsi untuk memiliki hubungan eksternal (tidak dapat membuatnya statis)' salah. 'static' inside 'extern "C"' valid; entitas yang dideklarasikan memiliki hubungan internal, dan tidak memiliki hubungan bahasa.
Richard Smith
14
'semua jenis fungsi, nama fungsi dan nama variabel memiliki hubungan bahasa' juga salah. Hanya nama fungsi dan nama variabel dengan hubungan eksternal yang memiliki hubungan bahasa.
Richard Smith
9
Perhatikan itu extern "C" { int i; }adalah definisi. Ini mungkin bukan yang Anda maksudkan, di sebelah non-definisi void g(char);. Untuk menjadikannya non-definisi, Anda perlu extern "C" { extern int i; }. Di sisi lain, sintaksis satu deklarasi tanpa kawat gigi membuat deklarasi itu bukan definisi: extern "C" int i;sama denganextern "C" { extern int i; }
aschepler
327

Hanya ingin menambahkan sedikit info, karena saya belum melihatnya diposting.

Anda akan sering melihat kode dalam header C seperti:

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

Apa yang dicapai ini adalah memungkinkan Anda untuk menggunakan file header C dengan kode C ++ Anda, karena makro "__cplusplus" akan ditentukan. Tetapi Anda juga masih dapat menggunakannya dengan kode C lawas, di mana makro TIDAK didefinisikan, sehingga tidak akan melihat konstruk unik C ++.

Meskipun, saya juga melihat kode C ++ seperti:

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

yang saya bayangkan mencapai hal yang sama.

Tidak yakin jalan mana yang lebih baik, tetapi saya telah melihat keduanya.

Tidak ada
sumber
11
Ada perbedaan yang jelas. Dalam kasus yang pertama, jika Anda mengkompilasi file ini dengan kompiler gcc normal, ia akan menghasilkan objek di mana nama fungsi tidak hancur. Jika Anda kemudian menautkan objek C dan C ++ dengan tautan itu TIDAK akan menemukan fungsinya. Anda harus menyertakan file "legacy header" dengan kata kunci eksternal seperti pada blok kode kedua Anda.
Anne van Rossum
8
@ Anne: Kompiler C ++ akan mencari nama yang tidak bercampur juga, karena terlihat extern "C"di header). Ini bekerja dengan baik, menggunakan teknik ini berkali-kali.
Ben Voigt
20
@ Anne: Itu tidak benar, yang pertama juga baik-baik saja. Itu diabaikan oleh kompiler C, dan memiliki efek yang sama seperti yang kedua di C ++. Kompilator tidak peduli apakah itu bertemu extern "C"sebelum atau sesudahnya termasuk header. Pada saat ia mencapai kompiler, itu hanya satu aliran panjang teks yang telah diproses.
Ben Voigt
8
@ Anne, tidak, saya pikir Anda telah dipengaruhi oleh beberapa kesalahan lain di sumbernya, karena apa yang Anda gambarkan salah. Tidak ada versi yang g++salah tentang ini, untuk target apa pun, setidaknya dalam 17 tahun terakhir. Inti dari contoh pertama adalah bahwa tidak masalah apakah Anda menggunakan kompiler C atau C ++, tidak ada nama mangling akan dilakukan untuk nama-nama di extern "C"blok.
Jonathan Wakely
7
"mana yang lebih baik" - yang pasti, varian pertama lebih baik: Ini memungkinkan termasuk header secara langsung, tanpa persyaratan lebih lanjut, baik dalam kode C dan C ++. Pendekatan kedua adalah solusi untuk header C penulis lupa penjaga C ++ (tidak ada masalah, meskipun, jika ini ditambahkan setelahnya, deklarasi extern "C" bersarang diterima ...).
Aconcagua
267

Dekompilasi g++biner yang dihasilkan untuk melihat apa yang sedang terjadi

main.cpp

void f() {}
void g();

extern "C" {
    void ef() {}
    void eg();
}

/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }

Kompilasi dan bongkar output ELF yang dihasilkan:

g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cpp
readelf -s main.o

Outputnya berisi:

     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

Penafsiran

Kami melihat bahwa:

  • efdan egdisimpan dalam simbol dengan nama yang sama seperti dalam kode

  • simbol-simbol lainnya hancur. Mari kita pisahkan mereka:

    $ c++filt _Z1fv
    f()
    $ c++filt _Z1hv
    h()
    $ c++filt _Z1gv
    g()
    

Kesimpulan: kedua jenis simbol berikut ini tidak hancur:

  • didefinisikan
  • dideklarasikan tetapi tidak terdefinisi ( Ndx = UND), harus disediakan pada tautan atau waktu berjalan dari file objek lain

Jadi, Anda akan membutuhkan extern "C"keduanya saat menelepon:

  • C dari C ++: beri tahu g++untuk mengharapkan simbol tidak cacat yang dihasilkan olehgcc
  • C ++ dari C: beri tahu g++untuk menghasilkan simbol yang tidak bisa diubah untuk gccdigunakan

Hal-hal yang tidak bekerja di luar C

Menjadi jelas bahwa setiap fitur C ++ yang memerlukan nama mangling tidak akan berfungsi di dalam 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) { }
}

Minimal runnable C dari contoh C ++

Demi kelengkapan dan untuk newb di luar sana, lihat juga: Bagaimana cara menggunakan file sumber C dalam proyek C ++?

Memanggil C dari C ++ cukup mudah: setiap fungsi C hanya memiliki satu simbol yang tidak cacat, jadi tidak diperlukan kerja ekstra.

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

cc

#include "c.h"

int f(void) { return 1; }

Lari:

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

Tanpa extern "C"tautan gagal dengan:

main.cpp:6: undefined reference to `f()'

karena g++mengharapkan untuk menemukan hancur f, yang gcctidak menghasilkan.

Contoh di GitHub .

Minimal runnable C ++ dari contoh C

Memanggil C ++ dari C sedikit lebih sulit: kita harus secara manual membuat versi yang tidak cacat dari setiap fungsi yang ingin kita tampilkan.

Di sini kita menggambarkan bagaimana mengekspos kelebihan fungsi C ++ ke 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);
}

Lari:

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

Tanpa extern "C"itu gagal dengan:

main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'

karena g++dihasilkan simbol hancur yang gcctidak dapat ditemukan.

Contoh di GitHub .

Diuji di Ubuntu 18.04.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
21
Jawaban terbaik karena Anda 1) secara eksplisit menyebutkan bahwa extern "C" {membantu Anda memanggil fungsi C yang tidak berubah dari dalam program C ++ , serta fungsi C ++ yang tidak berubah dari dalam program C , yang jawaban lainnya tidak membuatnya begitu jelas, dan 2) karena Anda menunjukkan contoh berbeda dari setiap. Terima kasih!
Gabriel Staples
3
Saya sangat suka jawaban ini
selfboot
4
Hands down jawaban terbaik karena ini menunjukkan cara memanggil fungsi kelebihan beban dari c
Gaspa79
1
@JaveneCPPMcGowan apa yang membuat Anda berpikir bahwa saya memiliki guru C ++? :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
205

Dalam setiap program C ++, semua fungsi non-statis direpresentasikan dalam file biner sebagai simbol. Simbol-simbol ini adalah string teks khusus yang secara unik mengidentifikasi fungsi dalam program.

Di C, nama simbol sama dengan nama fungsi. Ini dimungkinkan karena dalam C tidak ada dua fungsi non-statis yang memiliki nama yang sama.

Karena C ++ memungkinkan overloading dan memiliki banyak fitur yang tidak disukai C - seperti kelas, fungsi anggota, spesifikasi pengecualian - tidak mungkin hanya menggunakan nama fungsi sebagai nama simbol. Untuk mengatasinya, C ++ menggunakan apa yang disebut dengan nama mangling, yang mengubah nama fungsi dan semua informasi yang diperlukan (seperti jumlah dan ukuran argumen) menjadi beberapa string yang tampak aneh yang hanya diproses oleh kompiler dan penghubung.

Jadi, jika Anda menentukan fungsi yang akan eksternal C, kompiler tidak melakukan mangling nama dengannya dan dapat langsung diakses menggunakan nama simbolnya sebagai nama fungsi.

Ini berguna saat menggunakan dlsym()dan dlopen()untuk memanggil fungsi-fungsi tersebut.

sud03r
sumber
apa yang kamu maksud dengan berguna? apakah nama simbol = nama fungsi akan membuat nama simbol yang diteruskan ke dlsym diketahui, atau hal lainnya?
Kesalahan
1
@ Kesalahan: ya. Pada dasarnya tidak mungkin dalam kasus umum untuk dlopen () pustaka bersama C ++ yang hanya diberi file header dan memilih fungsi yang tepat untuk dimuat. (Pada x86, ada spesifikasi nama-mangling yang diterbitkan dalam bentuk Itanium ABI yang semua kompiler x86 yang saya tahu gunakan untuk menguraikan nama fungsi C ++, tetapi tidak ada dalam bahasa yang mengharuskan ini.)
Jonathan Tomer
52

C ++ mangles nama fungsi untuk membuat bahasa berorientasi objek dari bahasa prosedural

Sebagian besar bahasa pemrograman tidak dibangun di atas bahasa pemrograman yang ada. C ++ dibangun di atas C, dan lebih jauh itu adalah bahasa pemrograman berorientasi objek yang dibangun dari bahasa pemrograman prosedural, dan untuk alasan itu ada ekspresi C ++ seperti extern "C"yang memberikan kompatibilitas ke belakang dengan C.

Mari kita lihat contoh berikut:

#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;
}

Kompiler AC tidak akan mengkompilasi contoh di atas, karena fungsi yang sama printMedidefinisikan dua kali (walaupun mereka memiliki parameter berbeda int avs char a).

gcc -o printMe printMe.c && ./printMe;
1 kesalahan. PrintMe didefinisikan lebih dari sekali.

Kompiler C ++ akan mengkompilasi contoh di atas. Itu tidak peduli yang printMedidefinisikan dua kali.

g ++ -o printMe printMe.c && ./printMe;

Ini karena kompiler C ++ secara implisit mengganti nama fungsi ( mangles ) berdasarkan parameternya. Di C, fitur ini tidak didukung. Namun, ketika C ++ dibangun di atas C, bahasa dirancang untuk berorientasi objek, dan diperlukan untuk mendukung kemampuan untuk membuat kelas yang berbeda dengan metode (fungsi) dengan nama yang sama, dan untuk menimpa metode ( metode override ) berdasarkan perbedaan parameter.

extern "C" mengatakan "jangan membuat nama fungsi C"

Namun, bayangkan kita memiliki file legacy C yang bernama "parent.c" yang includeberfungsi dari file legacy C lainnya, "parent.h", "child.h", dll. Jika file legacy "parent.c" dijalankan melalui kompiler C ++, maka nama-nama fungsi akan hancur, dan mereka tidak akan lagi cocok dengan nama-nama fungsi yang ditentukan dalam "parent.h", "child.h", dll - jadi nama-nama fungsi dalam file-file eksternal juga perlu hancur Mengelompokkan nama fungsi di seluruh program C yang kompleks, yang memiliki banyak dependensi, dapat menyebabkan kode rusak; jadi mungkin lebih mudah untuk memberikan kata kunci yang dapat memberitahu kompiler C ++ untuk tidak membuat nama fungsi.

Kata extern "C"kunci memberi tahu kompiler C ++ untuk tidak memotong-motong (mengganti nama) nama fungsi C.

Sebagai contoh:

extern "C" void printMe(int a);

Timbul
sumber
dapatkah kita tidak menggunakan extern "C"jika kita hanya memiliki dllfile? Maksud saya jika kita tidak memiliki file header dan hanya memiliki file sumber (hanya implementasi) dan penggunaan fungsinya melalui fungsi pointer. dalam kondisi ini, kami baru saja menggunakan fungsi (terlepas dari namanya).
BattleTested
@ tfmontague, bagi saya Anda berhasil melakukannya dengan benar! lurus di kepala.
Artanis Zeratul
29

Tidak ada header-C yang dapat dibuat kompatibel dengan C ++ dengan hanya membungkus extern "C". Ketika pengidentifikasi dalam konflik header-C dengan kata kunci C ++, kompiler C ++ akan mengeluh tentang hal ini.

Sebagai contoh, saya telah melihat kode berikut gagal dalam g ++:

extern "C" {
struct method {
    int virtual;
};
}

Agak masuk akal, tetapi sesuatu yang perlu diingat ketika porting C-code ke C ++.

Sander Mertens
sumber
14
extern "C"berarti menggunakan tautan C, seperti dijelaskan oleh jawaban lain. Itu tidak berarti "mengkompilasi konten sebagai C" atau apa pun. int virtual;tidak valid dalam C ++ dan menetapkan tautan yang berbeda tidak mengubah itu.
MM
1
... atau mode pada umumnya, kode apa pun yang mengandung kesalahan sintaks tidak akan dikompilasi.
Valentin Heinitz
4
@ValentinHeinitz secara alami, meskipun menggunakan "virtual" sebagai pengidentifikasi dalam C bukan kesalahan sintaks. Aku hanya ingin ke titik bahwa Anda tidak dapat secara otomatis menggunakan setiap C header C ++ dengan meletakkan extern "C" di sekitarnya.
Sander Mertens
28

Ini mengubah hubungan fungsi sedemikian rupa sehingga fungsi dapat dipanggil dari C. Dalam prakteknya itu berarti bahwa nama fungsi tidak hancur .

Mempekerjakan bahasa Rusia
sumber
3
Rusak adalah istilah yang umum digunakan ... Jangan percaya saya pernah melihat 'dihiasi' digunakan dengan makna ini.
Matthew Scharley
1
Microsoft (setidaknya sebagian) menggunakan dihiasi daripada hancur dalam dokumentasi mereka. mereka bahkan memberi nama alat mereka untuk menghapus dekorasi (alias membatalkan) nama undname.
René Nyffenegger
20

Ini menginformasikan kompiler C ++ untuk mencari nama-nama fungsi-fungsi dalam gaya-C ketika menghubungkan, karena nama-nama fungsi yang dikompilasi dalam C dan C ++ berbeda selama tahap menghubungkan.

Mark Rushakoff
sumber
12

extern "C" dimaksudkan untuk dikenali oleh kompiler C ++ dan untuk memberi tahu kompiler bahwa fungsi yang dicatat dikompilasi dalam gaya C. Sehingga saat menautkan, tautan ke versi fungsi yang benar dari C.

Flami
sumber
6

Saya menggunakan 'extern "C"' sebelumnya untuk file dll (dynamic link library) untuk membuat dll. Main () berfungsi "dapat diekspor" sehingga dapat digunakan nanti di executable lain dari dll. Mungkin contoh tempat saya dulu menggunakannya bisa bermanfaat.

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);
}
SturmCoder
sumber
4
Palsu. extern "C"dan __declspec(dllexport)tidak terkait. Dekorasi simbol kontrol sebelumnya, yang terakhir bertanggung jawab untuk membuat entri ekspor. Anda dapat mengekspor simbol menggunakan dekorasi nama C ++ juga. Selain benar-benar kehilangan inti pertanyaan ini, ada juga kesalahan lain dalam sampel kode. Untuk satu, maindiekspor dari DLL Anda tidak menyatakan nilai balik. Atau menelepon konvensi, dalam hal ini. Saat mengimpor, Anda mengaitkan konvensi panggilan acak ( WINAPI), dan menggunakan simbol yang salah untuk build 32-bit (seharusnya _mainatau _main@0). Maaf, -1.
IInspectable
1
Itu hanya berulang, bahwa Anda tidak tahu, apa yang Anda lakukan, tetapi melakukannya dengan cara ini tampaknya bekerja untuk Anda, untuk beberapa daftar platform target yang tidak diungkapkan. Anda tidak membahas masalah yang saya kemukakan dalam komentar saya sebelumnya. Ini masih suara turun, karena sangat salah (ada lagi, yang tidak cocok dalam satu komentar).
IInspectable
1
Posting jawaban pada jenis Stack Overflow menyiratkan, bahwa Anda tahu apa yang Anda lakukan. Ini diharapkan. Adapun upaya Anda "untuk mencegah tumpukan korupsi saat dijalankan" : Tanda tangan fungsi Anda menentukan nilai pengembalian tipe void*, tetapi implementasi Anda tidak mengembalikan apa pun. Itu akan terbang sangat baik ...
IInspectable
1
Jika Anda menerapkan sesuatu, yang tampaknya berhasil, karena keberuntungan murni, maka Anda jelas tidak tahu apa yang Anda lakukan ( sampel "kerja" Anda termasuk dalam kategori itu). Ini perilaku yang tidak terdefinisi, dan tampaknya berfungsi adalah bentuk perilaku undefined yang valid. Itu masih belum ditentukan. Saya akan sangat menghargainya, jika Anda lebih rajin berolahraga di masa depan. Sebagian dari itu bisa menghapus jawaban yang diusulkan ini.
IInspectable
1
Anda menafsirkan kembali fungsi yang tidak mengembalikan apa pun sebagai fungsi yang mengembalikan pointer. Ini adalah keberuntungan murni, bahwa x86 sangat memaafkan sehubungan dengan tanda tangan fungsi yang tidak cocok, dan khususnya nilai pengembalian tipe integral. Kode Anda hanya berfungsi secara kebetulan. Jika Anda tidak setuju, Anda perlu menjelaskan, mengapa kode Anda bekerja dengan andal.
IInspectable
5

extern "C"adalah spesifikasi tautan yang digunakan untuk memanggil fungsi C dalam file sumber Cpp . Kita dapat memanggil fungsi C, menulis Variabel, & menyertakan tajuk . Fungsi dinyatakan dalam entitas eksternal & itu didefinisikan di luar. Sintaksnya adalah

Tipe 1:

extern "language" function-prototype

Tipe 2:

extern "language"
{
     function-prototype
};

misalnya:

#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)
{
    //
    //
}
Yogeesh HT
sumber
3

Jawaban ini untuk tenggat waktu yang tidak sabar / harus dipenuhi, hanya sebagian / penjelasan sederhana di bawah ini:

  • di C ++, Anda dapat memiliki nama yang sama di kelas melalui overloading (misalnya, karena mereka semua nama yang sama tidak dapat diekspor apa adanya dari dll, dll.) solusi untuk masalah ini adalah mereka dikonversi ke string yang berbeda (disebut simbol ), simbol mencatat nama fungsi, juga argumen, sehingga masing-masing fungsi ini bahkan dengan nama yang sama, dapat diidentifikasi secara unik (juga disebut, nama mangling)
  • di C, Anda tidak memiliki kelebihan, nama fungsi unik (jadi, string terpisah untuk mengidentifikasi nama fungsi secara unik tidak diperlukan, jadi simbol adalah nama fungsi itu sendiri)

Jadi
di C ++, dengan nama mangling identitas unik setiap fungsi
di C, bahkan tanpa nama mangling identitas unik setiap fungsi

Untuk mengubah perilaku C ++, yaitu, untuk menentukan bahwa nama mangling tidak boleh terjadi untuk fungsi tertentu, Anda dapat menggunakan extern "C" sebelum nama fungsi, untuk alasan apa pun, seperti mengekspor fungsi dengan nama tertentu dari dll. , untuk digunakan oleh kliennya.

Baca jawaban lain, untuk jawaban yang lebih detail / lebih benar.

Manohar Reddy Poreddy
sumber
1

Saat mencampurkan C dan C ++ (mis., Memanggil fungsi C dari C ++; dan b. Memanggil fungsi C ++ dari C), susunan nama C ++ menyebabkan masalah penghubungan. Secara teknis, masalah ini terjadi hanya ketika fungsi callee telah dikompilasi menjadi biner (kemungkinan besar, file * .a library) menggunakan kompiler yang sesuai.

Jadi kita perlu menggunakan extern "C" untuk menonaktifkan nama mangling di C ++.

Trombe
sumber
0

Tanpa bertentangan dengan jawaban bagus lainnya, saya akan menambahkan sedikit contoh saya.

Apa sebenarnya yang dilakukan oleh C ++ Compiler : ia membuat nama-nama dalam proses kompilasi, oleh karena itu kami perlu memberi tahu kompiler untuk memperlakukan C implementasi secara khusus.

Saat kami membuat kelas C ++ dan menambahkan extern "C", kami memberi tahu kompiler C ++ kami bahwa kami menggunakan konvensi pemanggilan C.

Alasan (kami memanggil implementasi C dari C ++): kami ingin memanggil fungsi C dari C ++ atau memanggil fungsi C ++ dari C (kelas C ++ ... dll tidak bekerja di C).

Susobhan Das
sumber
Selamat datang di Stack Overflow. Jika Anda memutuskan untuk menjawab pertanyaan yang lebih lama yang sudah mapan dan jawaban yang benar, menambahkan jawaban baru di akhir hari mungkin tidak memberi Anda kredit apa pun. Jika Anda memiliki beberapa informasi baru yang berbeda, atau Anda yakin jawaban lain semuanya salah, tentu saja tambahkan jawaban baru, tetapi 'jawaban lain' memberikan informasi dasar yang sama lama setelah pertanyaan diajukan biasanya dimenangkan ' tidak memberi Anda banyak kredit. Terus terang, saya tidak berpikir ada yang baru dalam jawaban ini.
Jonathan Leffler
Ya saya harus ingat maksud Anda - oke
Susobhan Das
-1

Fungsi void f () dikompilasi oleh kompiler C dan fungsi dengan nama yang sama void f () dikompilasi oleh kompiler C ++ bukan fungsi yang sama. Jika Anda menulis fungsi itu dalam C, dan kemudian Anda mencoba memanggilnya dari C ++, maka linker akan mencari fungsi C ++ dan tidak menemukan fungsi C.

extern "C" memberi tahu kompiler C ++ bahwa Anda memiliki fungsi yang dikompilasi oleh kompiler C. Setelah Anda mengatakan bahwa itu dikompilasi oleh kompiler C, kompiler C ++ akan tahu bagaimana memanggilnya dengan benar.

Ini juga memungkinkan kompiler C ++ untuk mengkompilasi fungsi C ++ sedemikian rupa sehingga kompiler C dapat memanggilnya. Fungsi itu secara resmi akan menjadi fungsi C, tetapi karena dikompilasi oleh kompiler C ++, ia dapat menggunakan semua fitur C ++ dan memiliki semua kata kunci C ++.

gnasher729
sumber
Kompiler C ++ dapat mengkompilasi suatu extern "C"fungsi - dan (tergantung pada beberapa batasan), ia akan dapat dipanggil dengan kode yang dikompilasi oleh kompiler C.
Jonathan Leffler