Cara menggunakan kata kunci eksternal dengan benar di C

236

Pertanyaan saya adalah kapan fungsi harus direferensikan dengan externkata kunci dalam C.

Saya gagal melihat kapan ini harus digunakan dalam praktek. Saat saya menulis sebuah program, semua fungsi yang saya gunakan tersedia melalui file header yang saya sertakan. Jadi mengapa bermanfaat untuk externmendapatkan akses ke sesuatu yang tidak terpapar dalam file header?

Saya mungkin berpikir tentang cara externkerjanya yang salah, dan jika demikian tolong perbaiki saya.

Sunting: Haruskah Anda melakukan externsesuatu ketika itu adalah deklarasi default tanpa kata kunci dalam file header?

lillq
sumber
terkait untuk fungsi: stackoverflow.com/questions/856636/… untuk varables: stackoverflow.com/questions/1433204
Ciro Santilli 郝海东 冠状 病 六四 六四 事件 法轮功

Jawaban:

290

" extern" mengubah tautannya. Dengan kata kunci, fungsi / variabel diasumsikan tersedia di tempat lain dan penyelesaiannya ditunda ke tautan.

Ada perbedaan antara "extern" pada fungsi dan pada variabel: pada variabel itu tidak instantiate variabel itu sendiri, yaitu tidak mengalokasikan memori apa pun. Ini perlu dilakukan di tempat lain. Karenanya, penting jika Anda ingin mengimpor variabel dari tempat lain. Untuk fungsi, ini hanya memberi tahu kompiler bahwa hubungan adalah eksternal. Karena ini adalah default (Anda menggunakan kata kunci "statis" untuk menunjukkan bahwa suatu fungsi tidak terikat menggunakan hubungan eksternal) Anda tidak perlu menggunakannya secara eksplisit.

bluebrother
sumber
1
lalu mengapa hal eksternal yang sama ada di Git: perangkat lunak yang sangat populer dan modern memeriksanya: github.com/git/git/blob/master/strbuf.h
rsjethani
K&R tidak mencatat bahwa itu adalah default untuk menyatakan fungsi sebagai "extern", namun jawaban ini menyelesaikan kebingungan saya!
Acgtyrant
@rsjethani Saya pikir ini untuk membuat dokumen lebih ketat dan format.
Acgtyrant
Mungkin pertanyaan bodoh, tapi bagaimana ini dibandingkan dengan meneruskan pernyataan?
weberc2
197

extern memberi tahu kompiler bahwa data ini didefinisikan di suatu tempat dan akan terhubung dengan tautan.

Dengan bantuan tanggapan di sini dan berbicara dengan beberapa teman di sini adalah contoh praktis dari penggunaan eksternal .

Contoh 1 - untuk menunjukkan perangkap:

File stdio.h:

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

myCFile1.c:
#include <stdio.h>

Code...

myCFile2.c:
#include <stdio.h>

Code...

Jika myCFile1.o dan myCFile2.o ditautkan, masing-masing file c memiliki salinan errno yang terpisah . Ini adalah masalah karena errno yang sama seharusnya tersedia di semua file yang ditautkan.

Contoh 2 - Perbaikan.

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

Sekarang jika myCFile1.o dan MyCFile2.o keduanya ditautkan oleh linker, keduanya akan menunjuk ke errno yang sama . Dengan demikian, penyelesaian implementasinya dengan eksternal .

lillq
sumber
71
Masalahnya bukan bahwa modul myCFile1 dan myCFile2 memiliki salinan errno yang terpisah, tetapi keduanya mengekspos simbol yang disebut "errno". Ketika tautan melihat ini, ia tidak tahu "errno" mana yang harus dipilih, sehingga itu akan diselamatkan dengan pesan kesalahan.
cwick
2
apa arti sebenarnya "ditautkan oleh tautan"? semua orang menggunakan istilah ini, saya tidak menemukan definisi :(
Marcel Falliere
7
@MarcelFalliere Wiki ~ Compiler mengkompilasi setiap file sumber sendiri dan membuat file objek untuk setiap file sumber. Linker menautkan file objek ini ke 1 yang dapat dieksekusi.
Bitterblue
1
@cwick gcc tidak memberikan kesalahan atau peringatan bahkan setelah menggunakan -Walldan -pedantic. Mengapa dan bagaimana ?
b-ak
6
Bukankah penjaga termasuk melindungi terhadap hal yang tepat ini?
obskyr
32

Telah dinyatakan bahwa externkata kunci berlebihan untuk fungsi.

Adapun variabel yang dibagikan di seluruh unit kompilasi, Anda harus mendeklarasikannya dalam file header dengan kata kunci extern, lalu mendefinisikannya dalam file sumber tunggal, tanpa kata kunci extern. File sumber tunggal harus yang berbagi nama file header, untuk praktik terbaik.

aib
sumber
@aib "redundant for functions", periksa komentar saya di jawaban bluebrother.
rsjethani
Bagaimana jika Anda tidak ingin mengekspos salah satu fungsi di file header? Bukankah lebih baik mendeklarasikan variabel dalam satu file C dan mengaksesnya dengan eksternal di file lain; biarkan linker menyelesaikan masalah dan sembunyikan sisa header.
ste3e
16

Bertahun-tahun kemudian, saya menemukan pertanyaan ini. Setelah membaca setiap jawaban dan komentar, saya pikir saya bisa mengklarifikasi beberapa detail ... Ini bisa berguna bagi orang-orang yang datang ke sini melalui pencarian Google.

Pertanyaannya secara khusus tentang penggunaan fungsi "eksternal", jadi saya akan mengabaikan penggunaan "eksternal" dengan variabel global.

Mari kita tentukan 3 prototipe fungsi:

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

File header dapat digunakan oleh kode sumber utama sebagai berikut:

//--------------------------------------
//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;

Untuk mengkompilasi dan menautkan, kita harus mendefinisikan "function_2" dalam file kode sumber yang sama tempat kita memanggil fungsi itu. Dua fungsi lainnya dapat didefinisikan dalam kode sumber yang berbeda " .C" atau mereka dapat ditemukan di file biner apa pun ( .OBJ, * .LIB, * .DLL), yang mungkin tidak memiliki kode sumbernya.

Mari kita sertakan lagi header "my_project.H" dalam file "* .C" yang berbeda untuk lebih memahami perbedaannya. Dalam proyek yang sama, kami menambahkan file berikut:

//--------------------------------------
//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;

Fitur penting untuk diperhatikan:

  • Ketika suatu fungsi didefinisikan sebagai "statis" dalam file header, kompiler / linker harus menemukan contoh fungsi dengan nama itu di setiap modul yang menggunakan file yang menyertakan itu.

  • Fungsi yang merupakan bagian dari pustaka C dapat diganti hanya dalam satu modul dengan mendefinisikan ulang prototipe dengan "statis" hanya dalam modul itu. Misalnya, ganti panggilan apa pun ke "malloc" dan "gratis" untuk menambahkan fitur deteksi kebocoran memori.

  • Specifier "extern" sebenarnya tidak diperlukan untuk fungsi. Ketika "statis" tidak ditemukan, suatu fungsi selalu dianggap "eksternal".

  • Namun, "extern" bukan default untuk variabel. Biasanya, setiap file header yang mendefinisikan variabel agar terlihat di banyak modul perlu menggunakan "extern". Satu-satunya pengecualian adalah jika file header dijamin akan disertakan dari satu dan hanya satu modul.

    Banyak manajer proyek kemudian mengharuskan variabel tersebut ditempatkan di awal modul, bukan di dalam file header apa pun. Beberapa proyek besar, seperti emulator permainan video "Mame" bahkan mengharuskan variabel tersebut hanya muncul di atas fungsi pertama yang menggunakannya.

Christian Gingras
sumber
Jadi mengapa tepatnya fungsi statis memerlukan definisi versus yang eksternal? (Saya tahu ini terlambat 2 tahun, tetapi ini sebenarnya sangat membantu untuk memahami)
SubLock69
2
Definisi tersebut diperlukan jika Anda memanggil fungsi pada baris 100 dan menginisikannya pada baris 500. Baris 100 akan menyatakan prototipe tidak terdefinisi. Jadi, Anda menambahkan prototipe di dekat bagian atas.
Christian Gingras
15

Dalam C, 'extern' tersirat untuk prototipe fungsi, seperti prototipe menyatakan fungsi yang didefinisikan di tempat lain. Dengan kata lain, prototipe fungsi memiliki hubungan eksternal secara default; menggunakan 'extern' baik-baik saja, tetapi berlebihan.

(Jika diperlukan tautan statis, fungsi tersebut harus dinyatakan sebagai 'statis' baik dalam prototipe maupun header fungsinya, dan ini biasanya harus keduanya dalam file .c yang sama).

Steve Melnikoff
sumber
8

Artikel yang sangat bagus yang saya sampaikan tentang externkata kunci, bersama dengan contoh-contohnya: http://www.geeksforgeeks.org/understanding-extern-keyword-in-c/

Meskipun saya tidak setuju bahwa menggunakan externdeklarasi fungsi adalah berlebihan. Ini seharusnya menjadi pengaturan kompiler. Jadi saya sarankan menggunakan externdeklarasi fungsi pada saat dibutuhkan.

tozak
sumber
3
Saya telah membaca artikel geeksforgeeks.org sebelum saya datang ke sini, tetapi menemukan bahwa itu ditulis dengan buruk. Terlepas dari kekurangan gramatikal dan sintaksis, ia menggunakan banyak kata untuk membuat poin yang sama beberapa kali dan kemudian membaca informasi penting. Misalnya, dalam Contoh 4, tiba-tiba 'somefile.h' dimasukkan, tetapi tidak ada yang dikatakan tentang hal itu selain: "Misalkan somefile.h memiliki definisi var". Nah, informasi yang kami "anggap" kebetulan adalah informasi yang saya cari. Sayangnya, tidak ada jawaban di halaman ini yang jauh lebih baik.
Elise van Looij
6

Jika setiap file dalam program Anda pertama kali dikompilasi ke file objek, maka file objek dihubungkan bersama, Anda perlu extern. Ini memberitahu kompiler "Fungsi ini ada, tetapi kode untuk itu ada di tempat lain. Jangan panik."

Chris Lutz
sumber
Um, begitulah biasanya terjemahan dilakukan: file sumber dikompilasi ke file objek, dan kemudian ditautkan. Kapan Anda tidak perlu eksternal dalam kasus itu? Anda juga tidak akan menggunakan #include untuk mendapatkan fungsi, melainkan prototipe fungsi. Saya tidak mengerti apa yang Anda bicarakan.
David Thornley
Saya tampaknya mengalami masalah ini akhir-akhir ini karena salah membaca. Maaf soal itu. Ketika saya masih baru di C, saya akan #include "file.c" untuk hanya menyertakan fungsi dalam satu file langsung ke file lain. Lalu saya menemukan cara menggunakan 'extern'. Saya pikir dia membuat kesalahan yang sama dengan saya.
Chris Lutz
4

Seharusnya semua deklarasi fungsi dan variabel dalam file header extern.

Pengecualian untuk aturan ini adalah fungsi sebaris yang didefinisikan di header dan variabel yang - walaupun didefinisikan dalam header - harus bersifat lokal ke unit terjemahan (file sumber yang dimasukkan header): ini seharusnya static.

Dalam file sumber, externtidak boleh digunakan untuk fungsi dan variabel yang ditentukan dalam file. Awali definisi lokal dengan staticdan tidak melakukan apa pun untuk definisi bersama - mereka akan menjadi simbol eksternal secara default.

Satu-satunya alasan untuk menggunakan externsama sekali dalam file sumber adalah untuk menyatakan fungsi dan variabel yang didefinisikan dalam file sumber lain dan yang tidak ada file header disediakan.


Mendeklarasikan prototipe fungsi externsebenarnya tidak perlu. Beberapa orang tidak menyukainya karena itu hanya akan membuang-buang ruang dan fungsi deklarasi sudah memiliki kecenderungan untuk meluap batas garis. Orang lain menyukainya karena cara ini, fungsi dan variabel dapat diperlakukan dengan cara yang sama.

Christoph
sumber
Bisakah Anda memberi alasan mengapa "Semua deklarasi fungsi dan variabel dalam file header harus eksternal."? Bagi saya dari tanggapan lain bahwa mereka eksternal secara default.
lillq
@Lane: externadalah opsional untuk deklarasi fungsi, tapi saya suka memperlakukan variabel dan fungsi dengan cara yang sama - setidaknya itu hal yang paling masuk akal yang bisa saya buat, karena saya tidak ingat persis mengapa saya mulai melakukan ini;)
Christoph
Bukankah lebih baik memasukkan variabel global ke dalam file C agar tidak terlihat oleh file C acak lain yang menyertakan header. Dan untuk selalu menggunakan extern di setiap global kecuali wastafel sebenarnya yang diinisialisasi sebagai masalah kejelasan; jika itu awalan eksternal maka itu didefinisikan di tempat lain.
ste3e
3

Fungsi yang sebenarnya didefinisikan dalam file sumber lain hanya boleh dideklarasikan dalam header. Dalam hal ini, Anda harus menggunakan eksternal ketika mendeklarasikan prototipe di header.

Sebagian besar waktu, fungsi Anda akan menjadi salah satu dari yang berikut (lebih seperti praktik terbaik):

  • statis (fungsi normal yang tidak terlihat di luar file .c itu)
  • inline statis (inline dari file .c atau .h)
  • extern (pernyataan dalam tajuk jenis berikutnya (lihat di bawah))
  • [tanpa kata kunci apa pun] (fungsi normal dimaksudkan untuk diakses menggunakan deklarasi eksternal)
Eduard - Gabriel Munteanu
sumber
Mengapa Anda ingin keluar ketika menyatakan prototipe jika ini adalah default?
lillq
@Lane: Mungkin sedikit bias, tetapi setiap proyek waras yang saya kerjakan menggunakan konvensi berikut: di header, deklarasikan prototipe hanya untuk fungsi eksternal (karenanya eksternal). Dalam file .c, prototipe polos dapat digunakan untuk meniadakan kebutuhan untuk pemesanan tertentu, tetapi mereka tidak harus ditempatkan di header.
Eduard - Gabriel Munteanu
1

Ketika Anda memiliki fungsi yang didefinisikan pada dll atau lib yang berbeda, sehingga kompiler mencari linker untuk menemukannya. Kasus khas adalah ketika Anda memanggil fungsi dari OS OS.

Otávio Décio
sumber