Harici “C” nin C ++ içindeki etkisi nedir?


1632

extern "C"C ++ koduna koymak tam olarak ne yapar ?

Örneğin:

extern "C" {
   void foo();
}

82
Size bu makaleyi tanıtmak istiyorum: http://www.agner.org/optimize/calling_conventions.pdf Size konvansiyon çağrısı ve derleyiciler arasındaki fark hakkında çok daha fazla şey anlatıyor.
Sam Liao

1
@Litherum Başımın üst kısmında, derleyiciye çapraz derleyiciniz olduğu düşünüldüğünde C'yi kullanarak bu kod kapsamını derlemesini söylüyor. Ayrıca, bu foo()işlevin bulunduğu bir Cpp dosyanız olduğu anlamına gelir .
ha9u63ar

1
@ ha9u63ar 'Başımın üstünden'. Yorumunuzun geri kalanı da yanlış. Silmenizi tavsiye ederim.
TamaMcGlinn

Yanıtlar:


1559

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" bir bağlantı spesifikasyonudur
  • Her derleyici olduğu gerekli "C" bağlantısını sağlamak için
  • bir bağlantı özelliği yalnızca ad alanı kapsamında gerçekleşir
  • tüm işlev türlerinin, işlev adlarının ve değişken adlarının dil bağlantısı vardır Richard'ın Yorumuna Bakın: Yalnızca dış bağlantıya sahip işlev adlarının ve değişken adlarının dil bağlantısı vardır
  • farklı dil bağlantılarına sahip iki işlev türü, aksi takdirde aynı olsa bile farklı türlerdir
  • bağlantı özellikleri yuva, iç bir son bağlantı belirler
  • extern "C" sınıf üyeleri için yok sayılır
  • belirli bir ada sahip en fazla bir işlev "C" bağlantısına sahip olabilir (ad alanına bakılmaksızın)
  • extern "C" bir fonksiyonu harici bir bağlantıya sahip olmaya zorlar (statik hale getiremez) Bkz . bu şekilde beyan edilen bir kuruluşun iç bağlantısı vardır ve bu nedenle dil bağlantısı yoktur
  • C ++ 'dan diğer dillerde tanımlanan nesnelere ve C ++' da diğer dillerden tanımlanan nesnelere bağlantı, uygulama tanımlı ve dile bağlıdır. Sadece iki dil uygulamasının nesne yerleşim stratejileri yeterince benzer olduğunda, bu bağlantı sağlanabilir

22
C derleyicisi, c ++ 'ın yaptığı mangling kullanmaz. Yani bir c ++ programından ac arayüzü çağırmak istiyorsanız, açıkça c arayüzü "extern c" olarak ilan etmek gerekir.
Sam Liao

59
@Faisal: çapraz referansların hepsi 'extern "C" olsa bile, farklı C ++ derleyicileriyle oluşturulan kodu bağlamaya çalışmayın. Sınıfların düzenleri veya istisnaları işlemek için kullanılan mekanizmalar veya değişkenlerin kullanımdan önce başlatılmasını sağlamak için kullanılan mekanizmalar veya diğer farklılıklar arasında genellikle farklılıklar vardır, ayrıca iki ayrı C ++ çalışma zamanı destek kitaplığına (biri için her derleyici).
Jonathan Leffler

8
'extern "C" bir işlevi harici bağlantıya sahip olmaya zorlar (statik hale getiremez)' yanlıştır. 'statik' iç 'extern "C"' geçerlidir; bu şekilde beyan edilen bir kuruluşun iç bağlantısı vardır ve bu nedenle bir dil bağlantısı yoktur.
Richard Smith

14
'tüm işlev türleri, işlev adları ve değişken adları bir dil bağlantısına sahiptir' de yanlıştır. Yalnızca işlev bağlantılarının ve harici bağlantıya sahip değişken adlarının dil bağlantısı vardır.
Richard Smith

9
Bunun 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; }
tanımsız

327

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.


11
Belirgin bir fark var. Birincisi durumunda, bu dosyayı normal gcc derleyicisiyle derlerseniz, işlev adının karıştırılmadığı bir nesne oluşturur. Daha sonra C ve C ++ nesnelerini linker ile bağlarsanız, fonksiyonları bulamaz. Bu "eski başlık" dosyalarını ikinci kod bloğunuzdaki gibi extern anahtar sözcüğüne eklemeniz gerekir.
Anne van Rossum

8
@Anne: C ++ derleyicisi extern "C", üstbilgide göründüğü için yönetilmeyen adları da arayacaktır ). Harika çalışıyor, bu tekniği birçok kez kullandı.
Ben Voigt

20
@Anne: Bu doğru değil, ilki de iyi. C derleyicisi tarafından yoksayılır ve C ++ 'da ikincisi ile aynı etkiye sahiptir. Derleyici 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.
Ben Voigt

8
@Anne, hayır, bence kaynaktaki başka bir hatadan etkilendiniz, çünkü açıkladığınız şey yanlış. Hiçbir sürümü 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 .
Jonathan Wakely

7
"hangisi daha iyi" - elbette, ilk varyant daha iyidir: Hem C hem de C ++ kodunda başka herhangi bir gereksinim olmadan doğrudan başlığın eklenmesine izin verir. İkinci yaklaşım, yazarın C ++ korumalarını unuttuğu C başlıkları için bir çözümdür (ancak, bunlar daha sonra eklenirse, iç içe extern "C" bildirimleri kabul edilir ...).
Aconcagua

267

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:

  • efve egkoddakiyle 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 :

  • tanımlanmış
  • Ndx = UNDbaş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 :

  • C ++ 'dan C: g++tarafından üretilen yönetilmeyen sembolleri beklemek söylegcc
  • C ++ C: kullanmak 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 fhangi gccüretmedi.

GitHub'da örnek .

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.

GitHub'da örnek .

Ubuntu 18.04'te test edildi.


21
En iyi yanıt Eğer 1) açıkça söz beri 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!
Gabriel Staples

3
Bu cevabı çok beğendim
selfboot

4
C'den

1
@JaveneCPPMcGowan bir C ++ öğretmenim olduğunu düşündüren nedir? :-)
Ciro Santilli 法轮功 病毒 at 六四 事件 法轮功

205

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 .


kullanışlı ile ne demek istiyorsun? name = işlev adı, dlsym'e iletilen sembol adını bilinen bir şey veya başka bir şey yapar mı?
Hata

1
@ Hata: evet. Genelde, sadece bir başlık dosyası verilen bir C ++ paylaşılan kütüphanesini dlopen () yapmak ve yüklemek için doğru işlevi seçmek aslında imkansızdır. (X86'da, C ++ işlev adlarını değiştirmek için kullandığımı bildiğim tüm x86 derleyicilerinin Itanium ABI biçiminde yayınlanmış bir ad yönetimi belirtimi var, ancak dilde hiçbir şey bunu gerektirmiyor.)
Jonathan Tomer

52

C ++, yordamsal bir dilden nesneye yönelik bir dil oluşturmak için işlev adlarını değiştirir

Ç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 printMeiki farklı şekilde tanımlanır (farklı parametreleri int avs 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" diyor

Ancak, 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 dlldosyamı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).
BattleTested

@tfmontague, benim için doğru çivilenmiş! düz kafa.
Artanis Zeratul

29

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.


14
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.
MM

1
... veya genel modda, sözdizimi hatası içeren kodlar derlenmez.
Valentin Heinitz

4
@ValentinHeinitz doğal olarak, C'de bir tanımlayıcı olarak "sanal" kullanmak bir sözdizimi hatası değildir. Ben sadece "ex" etrafında "C" koyarak C ++ herhangi bir C üstbilgisi otomatik kullanamazsınız işaret etmek istedim .
Sander Mertens

28

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 .


3
Mangled genel olarak kullanılan bir terimdir ... Bu anlamda kullanılan 'dekore edilmiş' gördüğümü hiç sanmıyorum.
Matthew Scharley

1
Microsoft (en azından kısmen) belgelerinde karışık olmak yerine dekore edilmiş kullanımlar kullanır . onlar bile bir ismi undecorate (nam-un mangle) olarak adlandırmak için kendi aracı undname.
René Nyffenegger

20

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.


12

extern "C", bir C ++ derleyicisi tarafından tanınmak ve derleyiciye belirtilen işlevin C stilinde derlendiğini (veya düzenleneceğini) bildirmek içindir. Böylece bağlantı yaparken, C'nin doğru işlev sürümüne bağlanır.


6

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

4
Sahte. 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, mainDLL'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 ( _mainveya olmalıdır _main@0). Üzgünüm -1.
2016

1
Bu sadece tekrarlanan, ne yaptığınızı bilmiyorsunuz, ancak bu şekilde yaptığınız, bazı açıklanmayan hedef platformlar listesi için sizin için çalışıyor gibi görünüyor. Önceki yorumumda gündeme getirdiğim sorunları ele almadınız. Bu, çılgınca yanlış olduğu için hala bir aşağı oylama (tek bir yoruma uymayan daha fazlası var).
öngörülemez

1
Yığın Taşması üzerine bir cevap göndermek, ne yaptığınızı bildiğiniz anlamına gelir. Bu bekleniyor. "Çalışma sırasında yığın bozulmasını engelleme" girişimi gelince : İşlev imzanız bir tür dönüş değeri belirtir void*, ancak uygulamanız hiçbir şey döndürmez. Yani ... gerçekten iyi uçuverin
IInspectable

1
Bir şey, o uygularsanız görünür saf şans çalışmalarına, o zaman açıkça do not ne yaptığını biliyorum (sizin "çalışma" Numune bu kategoriye giriyor). Tanımlanmamış bir davranıştır ve işe görünme, tanımlanmamış bir davranışın geçerli bir şeklidir. Hala tanımsız. Gelecekte daha fazla özen gösterdiyseniz, bunu çok takdir ediyorum. Bunun bir kısmı önerilen bu cevabı siliyor olabilir.
öngörülemez

1
Hiçbir şeyi döndürmeyen bir işlevi, işaretçiyi döndüren bir işlev olarak yeniden yorumluyorsunuz. Saf şans, x86'nın uyumsuz işlev imzaları ve özellikle de integral türünün dönüş değerleri açısından çok affedicidir. Kodunuz yalnızca tesadüf esasına göre çalışır. Kabul etmiyorsanız, kodunuzun neden güvenilir bir şekilde çalıştığını açıklamanız gerekir.
öngörülemez

5

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)
{
    //
    //
}

3

Bu cevap sabırsız / buluşma tarihleri ​​için, sadece bir kısmı / basit açıklaması aşağıdadır:

  • C ++ 'da, aşırı yükleme yoluyla sınıfta aynı ada sahip olabilirsiniz (örneğin, hepsi aynı isim olduğu için dll'den olduğu gibi dışa aktarılamaz, vb.) Bu sorunların çözümü farklı dizelere (sembol adı verilen) dönüştürülmesidir ), semboller işlevin adını, bağımsız değişkenleri de hesaba katar, bu nedenle bu işlevlerin her biri aynı adla bile benzersiz olarak tanımlanabilir (ad yönetimi) olarak da adlandırılır)
  • C'de aşırı yüklemeniz yoktur, işlev adı benzersizdir (bu nedenle, işlev adını benzersiz bir şekilde tanımlamak için ayrı bir dize gerekli değildir, bu nedenle sembol işlev adının kendisidir)

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.


1

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.


0

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).


Stack Overflow'a hoş geldiniz. İyi kurulmuş ve doğru cevapları olan daha eski bir soruyu cevaplamaya karar verirseniz, günün geç saatlerinde yeni bir yanıt eklemek size herhangi bir kredi getirmeyebilir. Bazı farklı yeni bilgileriniz varsa veya diğer cevapların yanlış olduğuna ikna olduysanız, elbette yeni bir cevap ekleyin, ancak aynı soruya genellikle sorulduktan uzun bir süre sonra aynı temel bilgileri veren 'yine başka bir cevap' kazandı ' Sana çok fazla kredi kazandırmaz. Açıkçası, bu cevapta yeni bir şey olduğunu sanmıyorum.
Jonathan Leffler


-1

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.


C ++ derleyicisi bir extern "C"işlevi derleyebilir - ve (bazı kısıtlamalara tabi olarak) bir C derleyicisi tarafından derlenen kodla çağrılabilir.
Jonathan Leffler
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.