extern "C"
C ++ koduna koymak tam olarak ne yapar ?
Örneğin:
extern "C" {
void foo();
}
foo()
işlevin bulunduğu bir Cpp dosyanız olduğu anlamına gelir .
extern "C"
C ++ koduna koymak tam olarak ne yapar ?
Örneğin:
extern "C" {
void foo();
}
foo()
işlevin bulunduğu bir Cpp dosyanız olduğu anlamına gelir .
Yanıtlar:
extern "C", C ++ 'da bir işlev adı yapar' C 'bağlantısı (derleyici adı değiştirmez), böylece istemci C kodu, yalnızca işlevinizin beyanı. İşlev tanımınız, istemci 'C' bağlayıcısının daha sonra 'C' adını kullanarak bağlanacağı ikili biçimde (C ++ derleyiciniz tarafından derlenmiştir) bulunur.
C ++ işlev adlarının aşırı yüklenmesine sahip olduğundan ve C içermediğinden, C ++ derleyicisi işlev adını bağlanmak için benzersiz bir kimlik olarak kullanamaz, bu nedenle bağımsız değişkenler hakkında bilgi ekleyerek adı yönetir. C'de işlev adlarını aşırı yükleyemediğiniz için AC derleyicisinin adı değiştirmesi gerekmez. Bir işlevin C ++ 'da extern "C" bağlantısı olduğunu belirtirseniz, C ++ derleyicisi, kullanılan ada argüman / parametre türü bilgisi eklemez bağı.
Bildiğiniz gibi, her bir bildiri / tanım için açıkça "C" bağlantısını belirtebilir veya belirli bir bağlantıya sahip olmak için bir dizi bildirim / tanım dizisini gruplandırmak için bir blok kullanabilirsiniz:
extern "C" void foo(int);
extern "C"
{
void g(char);
int i;
}
Teknikleri önemsiyorsanız, bunlar C ++ 03 standardının 7.5 bölümünde listelenmiştir, işte kısa bir özet ("C" extern vurgusu ile):
extern "C" { int i; }
bir tanım olduğunu unutmayın . Tanımlamamanın yanında bu sizin istediğiniz şey olmayabilir void g(char);
. Bunu tanımsız yapmak için ihtiyacınız olacak extern "C" { extern int i; }
. Öte yandan, parantez içermeyen tek bildirimli sözdizimi, bildirimi extern "C" int i;
extern "C" { extern int i; }
Henüz yayınlandığını görmediğim için biraz bilgi eklemek istedim.
C başlıklarında genellikle aşağıdaki gibi kod görürsünüz:
#ifdef __cplusplus
extern "C" {
#endif
// all of your legacy C code here
#ifdef __cplusplus
}
#endif
Bunun başardığı şey, bu C başlık dosyasını C ++ kodunuzla kullanmanıza izin vermesidir, çünkü "__cplusplus" makrosu tanımlanacaktır. Ama olabilir de yine makro edilir eski C kodu ile kullanmak DEĞİL kendisini benzersiz C ++ yapısını gözle görülmeyecek kadar, tanımlanmış.
Her ne kadar, ben de gibi C ++ kodu gördük:
extern "C" {
#include "legacy_C_header.h"
}
hayal ediyorum ki aynı şeyi başarıyor.
Hangi yolun daha iyi olduğundan emin değilim, ama ikisini de gördüm.
extern "C"
, üstbilgide göründüğü için yönetilmeyen adları da arayacaktır ). Harika çalışıyor, bu tekniği birçok kez kullandı.
extern "C"
, üstbilgiyi içermeden önce veya sonra karşılaşıp karşılaşmadığını daha az umursamadı . Derleyiciye ulaştığında, zaten önceden işlenmiş bir metin akışıdır.
g++
en azından son 17 yıl içinde herhangi bir zamanda, herhangi bir hedef için, Yanlış anladın. İlk örneğin asıl amacı, bir C veya C ++ derleyicisi kullanmanızın önemli olmamasıdır, extern "C"
bloktaki adlar için ad yönetimi yapılmaz .
g++
Neler olup bittiğini görmek için oluşturulan bir ikili dosyayı ayrıştırın
main.cpp
void f() {}
void g();
extern "C" {
void ef() {}
void eg();
}
/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }
Üretilen ELF çıkışını derleyin ve sökün :
g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cpp
readelf -s main.o
Çıktı şunları içerir:
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
yorumlama
Görüyoruz ki:
ef
ve eg
koddakiyle aynı ada sahip sembollerde saklandı
diğer semboller karışıktı. Onları kaldıralım:
$ c++filt _Z1fv
f()
$ c++filt _Z1hv
h()
$ c++filt _Z1gv
g()
Sonuç: aşağıdaki sembol türlerinin her ikisi de karışık değildi :
Ndx = UND
başka bir nesne dosyasından bağlantı veya çalışma zamanında sağlanacak şekilde bildirilmiş ancak tanımlanmamış ( )Bu nedenle extern "C"
, arama yaparken her ikisine de ihtiyacınız olacak :
g++
tarafından üretilen yönetilmeyen sembolleri beklemek söylegcc
g++
için yönetilmeyen semboller oluşturmak için söylegcc
Harici C'de çalışmayan şeyler
Ad yönetimi gerektiren herhangi bir C ++ özelliğinin içinde çalışmayacağı açıktır 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 ++ örneğinden minimum çalıştırılabilir C
Tamlık ve yeni başlayanlar için ayrıca bakınız: C kaynak dosyaları bir C ++ projesinde nasıl kullanılır?
C ++ 'dan C çağırmak oldukça kolaydır: her C fonksiyonunda sadece bir adet karışık olmayan sembol vardır, bu yüzden fazladan çalışma gerekmez.
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; }
Çalıştırmak:
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
Olmadan extern "C"
bağlantı başarısız:
main.cpp:6: undefined reference to `f()'
Çünkü g++
beklediği bulmak için bir mangled f
hangi gcc
üretmedi.
C örneğinden minimum çalıştırılabilir C ++
C ++ 'dan C' yi çağırmak biraz daha zordur: ortaya çıkarmak istediğimiz her işlevin mandallanmamış versiyonlarını manuel olarak yaratmamız gerekir.
Burada C ++ fonksiyon aşırı yüklenmelerini C'ye nasıl maruz bırakacağımızı göstereceğiz.
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);
}
Çalıştırmak:
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
Olmadan extern "C"
başarısız olur:
main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'
çünkü bulamayan g++
karışık semboller üretildi gcc
.
Ubuntu 18.04'te test edildi.
extern "C" {
çağrı yardımcı olan C ++ programlarından unmangled C fonksiyonları yanı sıra, C programlarından fonksiyonları ++ unmangled C diğer cevaplar çok açık yapmazlar, ve sen ayrı örneklerini göstermek için) 2 her biri. Teşekkürler!
Her C ++ programında, statik olmayan tüm işlevler ikili dosyada semboller olarak temsil edilir. Bu semboller, programdaki bir işlevi benzersiz şekilde tanımlayan özel metin dizeleridir.
C harfinde sembol adı işlev adıyla aynıdır. Bu mümkündür çünkü C'de iki statik olmayan fonksiyon aynı ada sahip olamaz.
C ++ aşırı yüklemeye izin verdiğinden ve C'nin sınıflar, üye işlevleri, istisna spesifikasyonları gibi pek çok özelliği olduğundan, işlev adını sembol adı olarak kullanmak mümkün değildir. Bunu çözmek için, C ++ işlev adını ve gerekli tüm bilgileri (bağımsız değişkenlerin sayısı ve boyutu gibi) yalnızca derleyici ve bağlayıcı tarafından işlenen bazı garip görünümlü dizeye dönüştüren ad yönetimi kullanır.
Bu nedenle, bir işlevi C ekseni olarak belirtirseniz, derleyici onunla ad yönetimi gerçekleştirmez ve işlev adı olarak sembol adı kullanılarak doğrudan erişilebilir.
Bu, bu işlevleri kullanırken dlsym()
ve dlopen()
çağırırken kullanışlıdır .
Çoğu programlama dili mevcut programlama dillerinin üzerine inşa edilmemiştir. C ++, C'nin üzerine inşa edilmiştir ve ayrıca prosedürel bir programlama dilinden inşa edilen nesne yönelimli bir programlama dilidir ve bu nedenle C extern "C"
ile geriye doğru uyumluluk sağlayan C ++ ifadeleri vardır.
Aşağıdaki örneğe bakalım:
#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 derleyici yukarıdaki örneği derlemez, çünkü aynı işlev printMe
iki farklı şekilde tanımlanır (farklı parametreleri int a
vs olmasına rağmen char a
).
gcc -o printMe printMe.c && ./printMe;
1 hatası. PrintMe bir kereden fazla tanımlanmıştır.
Bir C ++ derleyicisi yukarıdaki örneği derleyecektir. printMe
İki kez tanımlanmış olması umurumda değil .
g ++ -o printMe printMe.c && ./printMe;
Bunun nedeni, bir C ++ derleyicisinin işlevlerini parametrelerine dayalı olarak örtük olarak yeniden adlandırmasıdır ( mangles ). C'de bu özellik desteklenmemiştir. Ancak, C ++ C üzerine kurulduğunda, dil nesne yönelimli olacak şekilde tasarlanmıştır ve aynı ada sahip yöntemlerle (işlevler) farklı sınıflar oluşturma ve farklı yöntemlere göre yöntemleri geçersiz kılma ( yöntem geçersiz kılma ) yeteneğini desteklemek için gerekliydi. parametreleri.
extern "C"
"C işlev adlarını değiştirmeyin" diyorAncak, include
"parent.c" adında eski C dosyalarından, "parent.h", "child.h" vb. İşlev adlarının eski bir C dosyasına sahip olduğumuzu düşünün. Eski "parent.c" dosyası çalıştırılırsa bir C ++ derleyicisi aracılığıyla, işlev adları karıştırılır ve artık "parent.h", "child.h", vb. 'de belirtilen işlev adlarıyla eşleşmez - bu nedenle bu harici dosyalardaki işlev adlarının da karıştırmak. Çok sayıda bağımlılığı olan karmaşık bir C programında işlev adlarını değiştirmek, bozuk koda neden olabilir; bu nedenle C ++ derleyicisine bir işlev adını değiştirmemesini söyleyen bir anahtar kelime sağlamak uygun olabilir.
extern "C"
Anahtar kelime değil bozmak (adlandırma) C fonksiyon adları C ++ derleyicisi söyler.
Örneğin:
extern "C" void printMe(int a);
extern "C"
sadece bir dll
dosyamız varsa kullanamaz mıyız ? Yani bir başlık dosyamız yoksa ve sadece bir kaynak dosyamız varsa (sadece uygulamalar) ve fonksiyonunun fonksiyon işaretçisi aracılığıyla kullanılması durumunda. bu durumda, sadece fonksiyonlarını kullandık (isminden bağımsız olarak).
Herhangi bir C-başlığı, sadece "C" extern'e sarılarak C ++ ile uyumlu hale getirilemez. C ++ başlığındaki tanımlayıcılar C ++ anahtar sözcükleriyle çakıştığında, C ++ derleyicisi bu durumdan şikayet edecektir.
Örneğin, bir g ++ aşağıdaki kod başarısız gördük:
extern "C" {
struct method {
int virtual;
};
}
Tür mantıklı, ancak C-kodunu C ++ 'ya taşırken akılda tutulması gereken bir şey.
extern "C"
diğer cevaplar tarafından tarif edildiği gibi C bağlantısının kullanılması anlamına gelir. "İçeriği C olarak derlemek" veya başka bir şey demek değildir. int virtual;
C ++ 'da geçersizdir ve farklı bağlantı belirtmek bunu değiştirmez.
Bir işlevin bağlantısını, işlev C'den çağrılabilecek şekilde değiştirir. Pratikte, işlev adının karıştırılmayacağı anlamına gelir .
undname
.
Bağlama aşamasında C ve C ++ 'da derlenen işlevlerin adları farklı olduğu için, C ++ derleyicisine C stilinde bu işlevlerin adlarını aramasını bildirir.
Daha önce dll (dinamik bağlantı kitaplığı) dosyaları vb yapmak için 'extern "C"' kullanıldı. Belki onu kullandığım yere bir örnek yararlı olabilir.
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"
ve __declspec(dllexport)
ilgisiz. İlk sembol dekorasyonunu kontrol eder, ikincisi bir ihracat girişi oluşturmaktan sorumludur. C ++ ad dekorasyonunu kullanarak da bir sembolü dışa aktarabilirsiniz. Bu sorunun amacını tamamen kaçırmanın yanı sıra, kod örneğinde de başka hatalar var. Birincisi, main
DLL'nizden dışa aktarılan bir dönüş değeri bildirmez. Ya da konuyu çağıran bu konu için. İçe aktarırken, rastgele bir çağırma kuralı ( WINAPI
) ile ilişkilendirirsiniz ve 32 bitlik yapılar için yanlış sembolü kullanırsınız ( _main
veya olmalıdır _main@0
). Üzgünüm -1.
void*
, ancak uygulamanız hiçbir şey döndürmez. Yani ... gerçekten iyi uçuverin
extern "C"
için kullanılan bir bağlantı özelliğidir Cı arama işlevleri de CPP kaynak dosyaları . Biz yapabilirsiniz C fonksiyonları, yazma Değişkenler çağrı ve başlıkları ekleyin . İşlev, dış varlıkta bildirilir ve dışarıda tanımlanır. Sözdizimi:
Tip 1:
extern "language" function-prototype
Tip 2:
extern "language"
{
function-prototype
};
Örneğin:
#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)
{
//
//
}
Bu cevap sabırsız / buluşma tarihleri için, sadece bir kısmı / basit açıklaması aşağıdadır:
Yani
C ++ 'da, ad benzersiz bir şekilde kimlikler yöneterek
, C'deki her işlev , ad benzersiz bir şekilde kimlikler değiştirmeden bile
C ++ davranışını değiştirmek için, yani belirli bir işlev için ad yönetiminin olmaması gerektiğini belirtmek için extern "C" kullanabilirsiniz. , işlev adından önce , ne olursa olsun, bir işlevi belirli bir ada sahip bir dll'den dışa aktarmak gibi , müşterileri tarafından kullanılmak üzere.
Daha ayrıntılı / daha doğru cevaplar için diğer cevapları okuyun.
C ve C ++ (yani, C ++ 'dan C çağırma fonksiyonu ve C. C ++ fonksiyonunu C çağırma) karıştırılırken, C ++ ad yönetimi bağlantı sorunlarına neden olur. Teknik olarak konuşursak, bu sorun yalnızca callee işlevleri zaten ilgili derleyici kullanılarak ikili (büyük olasılıkla * .a kütüphane dosyası) olarak derlendiğinde oluşur.
Bu nedenle, C ++ 'da ad yönetimini devre dışı bırakmak için extern "C" kullanmamız gerekir.
Diğer iyi cevaplarla çelişmeden, örneğimin bir kısmını ekleyeceğim.
C ++ Derleyicisi tam olarak ne yapar: derleme işleminde adları yönetir, bu nedenle derleyiciye uygulamayı özel olarak ele almasını söylemeyi gerektirir C
.
C ++ sınıfları oluştururken ve eklerken extern "C"
, C ++ derleyicimize C çağrı kuralını kullandığımızı söylüyoruz.
Sebep (C ++ 'dan C uygulaması çağırıyoruz): ya C ++' dan C fonksiyonunu çağırmak istiyoruz ya da C'den C ++ fonksiyonunu çağırmak istiyoruz (C ++ sınıfları ... vs C 'de çalışmıyor).
C derleyicisi tarafından derlenen void f () işlevi ve C ++ derleyicisi tarafından derlenen void f () işleviyle aynı işlev aynı işlev değildir. Bu işlevi C olarak yazdıysanız ve sonra C ++ 'dan çağırmayı denediyseniz, bağlayıcı C ++ işlevini arar ve C işlevini bulamaz.
extern "C", C ++ derleyicisine C derleyicisi tarafından derlenen bir işleve sahip olduğunuzu bildirir. C derleyicisi tarafından derlendiğini söyledikten sonra, C ++ derleyicisi doğru olarak nasıl çağrılacağını bilecektir.
Ayrıca C ++ derleyicisinin bir C ++ işlevini C derleyicisinin arayabileceği şekilde derlemesini sağlar. Bu işlev resmen bir C işlevi olacaktır, ancak C ++ derleyicisi tarafından derlendiğinden, tüm C ++ özelliklerini kullanabilir ve tüm C ++ anahtar sözcüklerine sahiptir.
extern "C"
işlevi derleyebilir - ve (bazı kısıtlamalara tabi olarak) bir C derleyicisi tarafından derlenen kodla çağrılabilir.