Extern anahtar sözcüğü C doğru şekilde nasıl kullanılır


236

Benim sorum, bir işleve externC'deki anahtar kelimeyle ne zaman başvurulması gerektiği ile ilgilidir .

Bunun pratikte ne zaman kullanılması gerektiğini göremiyorum. Bir program yazarken kullandığım tüm işlevler dahil ettiğim başlık dosyaları aracılığıyla kullanılabilir. Peki extern, başlık dosyasında gösterilmeyen bir şeye erişmek neden yararlı olabilir ?

Nasıl externyanlış çalıştığını düşünebilirdim ve eğer öyleyse lütfen beni düzeltin.

Düzenleme:extern Bir başlık dosyasında anahtar kelime olmadan varsayılan bildirim olduğunda bir şey gerekir ?


Yanıtlar:


290

" extern" bağlantıyı değiştirir. Anahtar kelimeyle, işlev / değişkenin başka bir yerde kullanılabilir olduğu varsayılır ve çözümleme bağlayıcıya ertelenir.

Fonksiyonlar ve değişkenler üzerinde "extern" arasında bir fark vardır: değişkenlerde değişkenin kendisini başlatmaz, yani herhangi bir bellek ayırmaz. Bunun başka bir yerde yapılması gerekiyor. Bu nedenle, değişkeni başka bir yerden içe aktarmak istiyorsanız önemlidir. Fonksiyonlar için, bu sadece derleyiciye bağlantının extern olduğunu söyler. Bu varsayılan olduğundan (bir işlevin dış bağlantı kullanarak bağlı olmadığını belirtmek için "statik" anahtar sözcüğünü kullanırsınız) açıkça kullanmanız gerekmez.


1
o zaman Git'te
rsjethani

K & R, fonksiyonu "extern" olarak ilan etmenin varsayılan olduğunu unutmayın, ancak bu cevap karışıklığımı çözer!
acgtyrant

@rsjethani Belgeyi daha katı ve formatlı hale getirmek olduğunu düşünüyorum.
acgtyrant

Belki aptalca bir soru, ama bu ileri bildirimle nasıl karşılaştırılır?
weberc2

197

extern derleyiciye bu verinin bir yerde tanımlandığını ve bağlayıcıya bağlanacağını söyler.

Buradaki yanıtların yardımıyla ve burada birkaç arkadaşla konuşmak, extern kullanımının pratik bir örneğidir .

Örnek 1 - Bir tuzak göstermek için:

File stdio.h:

int errno;
/* other stuff...*/

myCFile1.c:
#include <stdio.h>

Code...

myCFile2.c:
#include <stdio.h>

Code...

MyCFile1.o ve myCFile2.o bağlıysa, c dosyaların her biri ayrı kopyalarını errno . Bu, aynı errno'nun tüm bağlı dosyalarda mevcut olması gerektiği için bir sorundur .

Örnek 2 - Düzeltme.

File stdio.h:

extern int errno;
/* other stuff...*/

File stdio.c

int errno;

myCFile1.c:
#include <stdio.h>

Code...

myCFile2.c:
#include <stdio.h>

Code...

Şimdi hem myCFile1.o hem de MyCFile2.o linker ile bağlıysa, ikisi de aynı errno'ya işaret edecektir . Böylece, extern ile uygulama çözme .


71
Sorun, myCFile1 ve myCFile2 modüllerinin ayrı bir errno kopyası olması değil, her ikisinin de "errno" adlı bir sembolü ortaya çıkarmasıdır. Bağlayıcı bunu gördüğünde, hangi "errno" 'yu ​​seçeceğini bilmez, bu yüzden bir hata mesajı ile kurtarılacaktır.
cwick

2
"linker ile bağlantılı" ne anlama geliyor? herkes bu terimi kullanıyor, herhangi bir tanım bulamıyorum :(
Marcel Falliere

7
@MarcelFalliere Wiki ~ Compiler her kaynak dosyasını tek başına derler ve her kaynak dosyası için bir nesne dosyası oluşturur. Bağlayıcı bu nesne dosyalarını 1 yürütülebilir dosyaya bağlar.
Bitterblue

1
@cwick gcc -Wallve kullandıktan sonra bile hata veya uyarı vermiyor -pedantic. Neden ? ve nasıl ?
b-ak

6
Dahil etme görevlisi bu tam şeye karşı koruma sağlamıyor mu?
obskyr

32

externAnahtar kelimenin işlevler için gereksiz olduğu zaten belirtilmişti .

Derleme birimleri arasında paylaşılan değişkenlere gelince, onları extern anahtar sözcüğü içeren bir başlık dosyasında bildirmeniz ve extern anahtar sözcüğü olmadan tek bir kaynak dosyada tanımlamanız gerekir. En iyi uygulama için tek kaynak dosya, başlık dosyasının adını paylaşan dosya olmalıdır.


@aib "işlevler için yedek", bluebrother'ın cevabındaki yorumuma bakın.
rsjethani

Üstbilgi dosyasındaki işlevlerden herhangi birini göstermek istemezseniz ne olur? Değişkeni bir C dosyasında bildirmek ve başka bir dosyaya extern tarafından erişmek daha iyi olmaz mıydı; linker sorunu çözsün ve başlığın geri kalanını gizlesin.
ste3e

16

Yıllar sonra bu soruyu keşfediyorum. Her yanıtı ve yorumu okuduktan sonra, birkaç ayrıntıya açıklık getirebileceğimi düşündüm ... Bu, Google aramasıyla buraya gelen insanlar için yararlı olabilir.

Soru özellikle "extern" fonksiyonlarını kullanmakla ilgilidir, bu yüzden global değişkenlerle "extern" kullanımını görmezden geleceğim.

3 işlev prototipi tanımlayalım:

//--------------------------------------
//Filename: "my_project.H"
extern int function_1(void);
static int function_2(void);
       int function_3(void);

Başlık dosyası, ana kaynak kodu tarafından aşağıdaki gibi kullanılabilir:

//--------------------------------------
//Filename: "my_project.C"
#include "my_project.H"

void main(void){
    int v1 = function_1();
    int v2 = function_2();
    int v3 = function_3();
}

int function_2(void) return 1234;

Derlemek ve bağlantı kurmak için, o işlevi çağırdığımız kaynak kod dosyasında "function_2" tanımlamalıyız. Diğer iki işlev, " .C" farklı kaynak kodunda tanımlanabilir veya kaynak koduna sahip olmayabileceğimiz herhangi bir ikili dosyada ( .OBJ, * .LIB, * .DLL) bulunabilir.

Farkı daha iyi anlamak için tekrar "my_project.H" başlığını farklı bir "* .C" dosyasına ekleyelim. Aynı projede, aşağıdaki dosyayı ekliyoruz:

//--------------------------------------
//Filename: "my_big_project_splitted.C"
#include "my_project.H"

void old_main_test(void){
    int v1 = function_1();
    int v2 = function_2();
    int v3 = function_3();
}

int function_2(void) return 5678;

int function_1(void) return 12;
int function_3(void) return 34;

Dikkat edilmesi gereken önemli özellikler:

  • Bir işlev bir başlık dosyasında "statik" olarak tanımlandığında, derleyici / bağlayıcı o dosyayı içeren her modülde bu ada sahip bir işlevin örneğini bulmalıdır.

  • C kütüphanesinin bir parçası olan bir işlev, yalnızca o modülde "statik" olan bir prototip yeniden tanımlanarak yalnızca bir modülde değiştirilebilir. Örneğin, bellek sızıntısı algılama özelliği eklemek için "malloc" ve "free" çağrılarını değiştirin.

  • "Extern" belirleyicisi işlevler için gerçekten gerekli değildir. "Statik" bulunmadığında, her zaman bir fonksiyonun "extern" olduğu varsayılır.

  • Ancak "extern" değişkenler için varsayılan değer değildir. Normalde, değişkenlerin birçok modülde görünür olmasını tanımlayan herhangi bir başlık dosyasının "extern" kullanması gerekir. Tek istisna, bir başlık dosyasının tek bir modülden dahil edilmesinin garanti edilmesi olabilir.

    Birçok proje yöneticisi daha sonra bu değişkenin herhangi bir başlık dosyasının içine değil modülün başına yerleştirilmesini isteyecektir. Video oyunu emülatörü "Mame" gibi bazı büyük projeler, bu değişkenin bunları kullanan ilk fonksiyonun üzerinde görünmesini bile gerektirir.


Öyleyse neden statik fonksiyonun harici fonksiyonlara karşı tam bir tanımlamaya ihtiyacı var? (Bunun 2 yıl geç olduğunu biliyorum, ama bu gerçekten anlamak için gerçekten yararlı)
SubLock69

2
İşlevi 100. satırda çağırır ve 500. satırda başlatırsanız tanım gerekir. 100. satır tanımsız prototip bildirir. Böylece, prototipi en üste eklersiniz.
Christian Gingras

15

C'de, bir prototip başka bir yerde tanımlanmış bir işlevi bildirdiği için, fonksiyon prototipleri için 'extern' ima edilir. Başka bir deyişle, bir işlev prototipinin varsayılan olarak harici bağlantısı vardır; 'extern' kullanmak iyidir, ancak gereksizdir.

(Statik bağlantı gerekiyorsa, işlev hem prototipinde hem de işlev başlığında 'statik' olarak bildirilmeli ve normalde her ikisi de aynı .c dosyasında olmalıdır).


8

externAnahtar kelimelerle ilgili olarak örneklerle birlikte geldiğim çok iyi bir makale : http://www.geeksforgeeks.org/understanding-extern-keyword-in-c/

Yine externde işlev bildirimlerinde kullanmanın gereksiz olduğunu kabul etmiyorum . Bu bir derleyici ayarı olması gerekiyordu. Bu nedenle extern, gerektiğinde işlev bildirimlerinde kullanılmasını öneririm .


3
Buraya gelmeden önce geeksforgeeks.org makalesini okudum, ancak oldukça kötü yazılmış buldum. Dilbilgisi ve sözdizimi eksikliklerinin yanı sıra, aynı noktayı birkaç kez yapmak için birçok kelime kullanır ve daha sonra kritik bilgileri gözden geçirir. Örneğin, Örnek 4'te, aniden 'somefile.h' eklenir, ancak bunun hakkında başka bir şey söylenmez: "somefile.h öğesinin var tanımına sahip olduğunu varsayalım". "Tahmin ettiğimiz" bilgiler, aradığım bilgiler. Ne yazık ki, bu sayfadaki cevapların hiçbiri daha iyi değil.
Elise van Looij

6

Programınızdaki her dosya ilk önce bir nesne dosyasına derlenmişse, nesne dosyaları birbirine bağlanır, ihtiyacınız vardır extern. Derleyiciye "Bu işlev var, ancak bunun kodu başka bir yerde. Panik yapmayın."


Um, normalde çeviri bu şekilde yapılır: kaynak dosyalar nesne dosyalarına derlenir ve sonra bağlanır. Bu durumda ne zaman extern'e ihtiyacınız olmaz? İşlevleri almak için #include yerine işlev prototipleri kullanırsınız. Ne hakkında konuştuğunu anlamıyorum.
David Thornley

Son zamanlarda bazı şeyleri yanlış anlamak gibi bir sorun yaşıyorum. Bunun için üzgünüm. C ile yeni tanıştığımda, sadece bir dosyadaki işlevleri doğrudan diğer dosyaya eklemek için "file.c" yi eklerdim. Sonra 'extern'i nasıl kullanacağımı anladım. Onunla aynı hatayı yaptığını sanıyordum.
Chris Lutz

4

Başlık dosyalarındaki tüm işlev ve değişken bildirimleri olmalıdır extern.

Bu kuralın istisnaları, başlıkta tanımlanan satır içi işlevler ve başlıkta tanımlansa da çeviri biriminde yerel olması gereken değişkenlerdir (başlığın dahil edildiği kaynak dosya): bunlar olmalıdır static.

Kaynak dosyalarda, externdosyada tanımlanan işlevler ve değişkenler için kullanılmamalıdır. Sadece yerel tanımları önekleyin staticve paylaşılan tanımlar için hiçbir şey yapmayın - varsayılan olarak harici semboller olacaktır.

externKaynak dosyada kullanılmanın tek nedeni , diğer kaynak dosyalarda tanımlanan ve başlık dosyası bulunmayan işlevleri ve değişkenleri bildirmektir.


İşlev prototiplerini bildirmek externaslında gereksizdir. Bazı insanlar bundan hoşlanmıyor, çünkü sadece alan israf edecek ve fonksiyon bildirimleri zaten hat sınırlarını aşma eğiliminde. Diğerleri bunu sever, çünkü bu şekilde işlevler ve değişkenler aynı şekilde tedavi edilebilir.


"Başlık dosyalarındaki işlevlerin ve değişkenlerin tüm bildirimlerinin açık olması" için bir neden verebilir misiniz? Diğer cevaplardan bana varsayılan olarak extern oldukları görünüyor.
lillq

@Lane: externmuamele değişkenler ve fonksiyonlar aynı yolu gibi işlev bildirimleri için isteğe bağlıdır, ama ben - Tam hatırlamıyorum olarak bunu yapmaya başladı neden, ile gelebilir en makul şey olduğunu en azından;)
Christoph

Başlığı içeren diğer rasgele C dosyaları tarafından görülmemesi için her zaman C değişkenine global değişkenler eklemek daha iyi bir fikir değil mi? Ve başlatılmış gerçek lavabo hariç her küreselde extern'i açıklık olarak kullanmak; extern'in önüne eklenmişse başka bir yerde tanımlanır.
ste3e

3

Diğer kaynak dosyalarda tanımlanmış olan işlevler yalnızca başlıklarda bildirilmelidir . Bu durumda, kullanmak gerekir extern zaman beyan bir başlıkta prototipi.

Çoğu zaman, işlevleriniz aşağıdakilerden biri olacaktır (daha çok en iyi uygulama gibi):

  • statik (bu .c dosyasının dışında görünmeyen normal işlevler)
  • statik satır içi (.c veya .h dosyalarından satır içi)
  • extern (bir sonraki tür başlıklarında beyan (aşağıya bakınız))
  • [hiçbir anahtar kelime yok] (dış işlev bildirimleri kullanılarak erişilmesi gereken normal işlevler)

Bu varsayılansa prototipi bildirirken neden dışarı çıkardınız?
lillq

@Lane: Biraz önyargılı olabilir, ancak üzerinde çalıştığım her aklı başında proje aşağıdaki kuralı kullanır: başlıklarda, prototipleri yalnızca dış işlevler için bildirir (dolayısıyla extern). .C dosyalarında, belirli sipariş gereksinimini ortadan kaldırmak için düz prototipler kullanılabilir, ancak üstbilgilere yerleştirilmemelidir.
Eduard - Gabriel Munteanu

1

Farklı bir dll veya lib üzerinde tanımlanmış bu işlevi varsa, böylece derleyici onu bulmak için linker savunur. Tipik durum, OS API'sinden işlevler aradığınızda ortaya çıkar.

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.