C ++ ve C'yi birleştirmek - #ifdef __cplusplus nasıl çalışır?


319

Eski C kodu çok olan bir proje üzerinde çalışıyorum . Sonunda eski kodu da dönüştürmek amacıyla C ++ ile yazmaya başladık. C ve C ++ ' nın nasıl etkileştiği hakkında biraz kafam karıştı . Ben sararak anlıyoruz C ile kodu extern "C"bozmak olmaz derleyici C ++ C kodun isimleri, ama tamamen emin bu nasıl uygulanacağı değilim.

Yani, her bir C başlık dosyasının üstünde (dahil etme korumalarından sonra),

#ifdef __cplusplus
extern "C" {
#endif

ve altta, yazıyoruz

#ifdef __cplusplus
}
#endif

İkisi arasında, tüm içerme, yazım tanımları ve işlev prototiplerimiz var. Bunu doğru anlayıp anlamadığımı görmek için birkaç sorum var:

  1. C başlık dosyası Bh içeren başka bir C başlık dosyası Ch içeren bir C ++ dosyası A.hh varsa , bu nasıl çalışır? Derleyici Bh adımlara __cplusplustanımlanır, bu yüzden kodu ile sarın extern "C" (ve __cplusplusbu blok içinde tanımlanmayacak) düşünüyorum. Bu nedenle, Ch'ye adım attığında __cplusplustanımlanmayacak ve kod sarılmayacaktır extern "C". Bu doğru mu?

  2. Bir kod parçasını kaydırmakla ilgili yanlış bir şey var mı extern "C" { extern "C" { .. } }? İkincisi extern "C" ne yapacak?

  3. Bu sarmalayıcıyı .c dosyalarının çevresine koymayız, sadece .h dosyalarının içine koyarız. Peki, bir fonksiyonun prototipi yoksa ne olur? Derleyici bunun bir C ++ işlevi olduğunu düşünüyor mu?

  4. Ayrıca C ile yazılmış ve çevresinde bu tür bir sarmalayıcı bulunmayan bazı üçüncü taraf kodları kullanıyoruz . Bu kütüphaneden bir başlık eklediğimde extern "C", #include'un çevresine koyuyorum . Bununla başa çıkmanın doğru yolu bu mu?

  5. Son olarak, bu kurmak iyi bir fikir mi? Yapmamız gereken başka bir şey var mı? Öngörülebilir gelecek için C ve C ++ 'ı karıştıracağız ve tüm üslerimizi kapsadığımızdan emin olmak istiyorum.



2
Kısacası, bu en iyi açıklama: To ensure that the names declared in that portion of code have C linkage, and thus C++ name mangling is not performed. ( Bağlantıdan aldım )
anhldbk

C dilinin adını koyu yazmanız gerekmez
Edward Karak

Yanıtlar:


290

extern "C"derleyicinin kodu okuma biçimini gerçekten değiştirmez. Kodunuz bir .c dosyasındaysa, C olarak derlenir. Bir .cpp dosyasındaysa, C ++ olarak derlenir (yapılandırmanızda garip bir şey yapmazsanız).

Yapan extern "C"şey bağlantıyı etkiler. C ++ işlevleri derlendiğinde adları karıştırılır - aşırı yüklemeyi mümkün kılan budur. İşlev adı, parametre türlerine ve sayısına göre değiştirilir, böylece aynı ada sahip iki işlev farklı sembol adlarına sahip olur.

Bir içindeki extern "C"kod hala C ++ kodudur. Bir dış "C" bloğunda neler yapabileceğiniz konusunda sınırlamalar vardır, ancak hepsi bağlantıyla ilgilidir. C bağlantısıyla oluşturulamayan yeni simgeler tanımlayamazsınız. Bu, örneğin hiçbir sınıf veya şablon anlamına gelmez.

extern "C"bloklar güzel yuva. Ayrıca extern "C++"kendinizi ümitsizce tuzağa düşürülmüş bulursanızextern "C" bölgelerin , ancak temizlik açısından iyi bir fikir değil.

Şimdi, özellikle numaralı sorularınızla ilgili olarak:

# 1 ile ilgili olarak: __cplusplus extern "C"blokların içinde tanımlanmış olarak kalacaktır . Yine de bu önemli değil, çünkü bloklar düzgün bir şekilde yuvalanmalıdır.

# 2 ile ilgili olarak: C ++ derleyicisi üzerinden çalıştırılan herhangi bir derleme birimi için __cplusplus tanımlanacaktır. Genellikle, bu .cpp dosyaları ve bu .cpp dosyası tarafından dahil edilen dosyalar anlamına gelir. Aynı .h (veya .hh veya .hpp veya neye sahip olduğunuz), farklı derleme birimleri bunları içeriyorsa, farklı zamanlarda C veya C ++ olarak yorumlanabilir. .H dosyasındaki prototiplerin C sembolü adlarına başvurmasını istiyorsanız, extern "C"C ++ olarak yorumlanırken sahip olmaları gerekir ve bu extern "C"nedenle C olarak yorumlanırken olmamalıdırlar #ifdef __cplusplus.

3. soruya cevap vermek için: prototipsiz fonksiyonlar bir extern "C"bloğun içinde değil .cpp dosyalarındaysa C ++ bağlantısına sahip olacaktır . Yine de, bu iyi, çünkü eğer prototip yoksa, sadece aynı dosyadaki diğer fonksiyonlar tarafından çağrılabilir ve daha sonra genellikle bağlantının neye benzediğini umursamazsınız, çünkü bu fonksiyona sahip olmayı planlamıyorsunuz yine de aynı derleme birimi dışındaki herhangi bir şey tarafından çağrılır.

# 4 için, tam olarak anladınız. C bağlantısı olan bir kod başlığı (C derleyicisi tarafından derlenen kod gibi) eklerseniz, o zaman extern "C"başlığa ihtiyacınız vardır - bu şekilde kütüphane ile bağlantı kurabilirsiniz. (Aksi takdirde, bağlayıcınız, aradığınız _Z1hiczamanki gibi adlara sahip işlevleri ararvoid h(int, char)

5: Bu tür bir karıştırma, yaygın bir kullanım nedenidir extern "C"ve bu şekilde yapmakta yanlış bir şey görmüyorum - sadece ne yaptığınızı anladığınızdan emin olun.


10
extern "C++"C ++ başlığınızın / kodunuzun bazı C kodlarının derinliklerine sıkıştığında bahsetmek için iyi
deddebme

1
Basit bir C programı yazdım. İçine #ifdef __cplusplus bloğu ekledim ve bir printf ("__ cplusplus tanımlı \ n") ekledim; içinde. Bunu gcc ile derlersem, "__cplusplus tanımlı" yazdırılmaz, ancak g ++ ile derlersem yazdırılır. Yani şekil __cplusplus derleyicinin C ++ derleyicisi olduğu anlamına gelir (söylediniz). Doğru değil mi? (çünkü '__cplusplus' un "C" blokları "içinde tanımlanması gerektiğini söyledim. __cplusplus'ı açıkça tanımlayabilir miyiz?
Chan Kim

1
İstediğiniz (neredeyse) her şeyi, bütün noktasını tanımlamak gerekir iken __cplusplusolmadığını belirlemektir C++vs kullanılıyor C, bu yüzden elle tanımlayan / açıkça ... bunun amacını aşıyor
nurchi

Extern "C", derleyicinin kaynak dosyayı nasıl görüntülediği ile ilgili değildir, tamamen başlık dosyasını nasıl görüntülediği ile ilgilidir. C vs C ++ olarak derlendiğinde yapıların farklı boyutları olabilir, tabii ki ad yönetimi ve potansiyel olarak başka farklılıklar da vardır.
Nick

39
  1. extern "C"__cplusplusmakronun varlığını veya yokluğunu değiştirmez . Sadece sarılmış beyanların bağlantısını ve isim yönetimini değiştirir.

  2. extern "C"Blokları oldukça mutlu bir şekilde iç içe geçirebilirsiniz .

  3. .cDosyalarınızı C ++ olarak derlerseniz, extern "C"blokta olmayan ve extern "C"prototip içermeyen herhangi bir şey C ++ işlevi olarak kabul edilir. Onları C olarak derlerseniz, elbette her şey bir C işlevi olacaktır.

  4. Evet

  5. C ve C ++ 'ı bu şekilde güvenle karıştırabilirsiniz.


Eğer derleme .cC gibi dosyaları ++, sonra her şey bir olsa bile, C ++ kod olarak derlendi extern "C"blokta. extern "C"Kod bütün gerektirdiği şeylerle birlikte, çağıran sözleşmeler (örneğin operatör yükleme) ama fonksiyonunun vücudun C ++ bağımlı özellikler hala C ++ olarak derlenmektedir kullanamaz.
David C.

21

Andrew Shelansky'nin mükemmel cevabına ve biraz katılmamaya yardımcı olan birkaç gotchas , derleyicinin kodu okuma şeklini gerçekten değiştirmez

İşlev prototipleriniz C olarak derlendiğinden, aynı işlev adlarını farklı parametrelerle aşırı yükleyemezsiniz - bu, derleyicinin ad yönetiminin temel özelliklerinden biridir. Bir bağlantı sorunu olarak tanımlanır, ancak bu doğru değildir - hem derleyiciden hem de bağlayıcıdan hatalar alırsınız.

Derleyici hataları, aşırı yükleme gibi prototip bildiriminin C ++ özelliklerini kullanmaya çalışırsanız olacaktır.

Fonksiyonun görünecektir çünkü eğer bağlayıcı hataları bulunamadı daha sonra ortaya çıkar değil sahip extern "C" bildirimleri etrafında sarıcı ve başlık C ve C ++ kaynağının karışımı dahildir.

İnsanların C ++ derlemesi olarak C derlemesini kullanmalarını engellemenin bir nedeni , kaynak kodlarının artık taşınabilir olmadığı anlamına gelir. Bu ayar bir proje ayarıdır ve bu nedenle .c dosyası başka bir projeye bırakılırsa, c ++ olarak derlenmez. İnsanların dosya eklerini .cpp olarak yeniden adlandırmak için zaman ayırmasını tercih ederim.


1
Bu şifreli bir sebepti, saçlarımı çekiyordu. Gerçekten bir yere gönderilmesi gerekiyor.
Mitchell Currie

3

Hem C hem de C ++ uygulamasının C arayüzlerini sorunsuz kullanmasına izin vermek için ABI ile ilgili.

C dili çok kolay olduğundan, GCC, Borland C \ C ++, MSVC vb. Gibi farklı derleyiciler için kod üretimi uzun yıllar boyunca kararlıydı.

C ++ gittikçe daha popüler hale gelirken, yeni C ++ etki alanına çok şey eklenmelidir (örneğin, Cfront AT&T'de terk edildi, çünkü C ihtiyaç duyduğu tüm özellikleri kapsayamadı). Bu şekilde bir şablon geçmişten özelliği ve derleme zamanı kod oluşturma, farklı derleyici aslında ayrı ayrı C ++ derleyici ve bağlayıcı fiili uygulamasını yaptığımız satıcıları, fiili ABI farklı platformlarda C ++ programa hiç uyumlu değildir.

İnsanlar hala C ++ 'da gerçek programı uygulamak isteyebilirsiniz ama yine de eski C arayüzü ve ABI her zamanki gibi tutmak, başlık dosyası extern "C" {} ilan etmek zorunda, derleyicinin uyumlu / eski / basit / kolay C ABI oluşturmasını söyler Derleyici C ++ derleyicisi değilse, derleyici işlevleri için .

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.