Membuat fungsi staticmenyembunyikannya dari unit terjemahan lain, yang membantu menyediakan enkapsulasi .
helper_file.c
int f1(int);/* prototype */staticint f2(int);/* prototype */int f1(int foo){return f2(foo);/* ok, f2 is in the same translation unit *//* (basically same .c file) as f1 */}int f2(int foo){return42+ foo;}
main.c :
int f1(int);/* prototype */int f2(int);/* prototype */int main(void){
f1(10);/* ok, f1 is visible to the linker */
f2(12);/* nope, f2 is not visible to the linker */return0;}
Apakah unit terjemahan terminologi yang tepat untuk digunakan di sini? Bukankah file objek lebih akurat? Dari apa yang saya mengerti, fungsi statis disembunyikan dari linker dan linker tidak beroperasi pada unit terjemahan.
Steven Eckhoff
2
Seharusnya saya juga mengatakan, bahwa saya suka menganggapnya sebagai disembunyikan dari linker; sepertinya lebih jelas seperti itu.
Steven Eckhoff
1
jadi, fungsi internal (yang kita yakin tidak menyebutnya di luar file c-nya), kita harus meletakkannya sebagai fungsi statis, kan? Jadi, kami yakin tidak bisa menelepon ke tempat lain. Terima kasih :)
hqt
1
Bagaimana Anda mengkompilasi ini? Apakah Anda menggunakan #include <helper_file.c>? Saya pikir itu akan menjadikannya unit terjemahan tunggal ...
Atcold
2
@Atcold: cara saya menulis kode Anda cukup memasukkan 2 file sumber di baris perintah, seperti ini gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c. Prototipe untuk fungsi hadir di kedua file sumber (tidak perlu untuk file header). Tautan akan menyelesaikan fungsi.
pmg
80
pmg tepat tentang enkapsulasi; selain menyembunyikan fungsi dari unit terjemahan lain (atau lebih tepatnya, karena itu), membuat fungsi staticjuga dapat memberi manfaat kinerja dengan adanya optimisasi kompiler.
Karena suatu staticfungsi tidak dapat dipanggil dari mana saja di luar unit terjemahan saat ini (kecuali jika kode membawa pointer ke alamatnya), kompiler mengontrol semua titik panggilan ke dalamnya.
Ini berarti bahwa bebas untuk menggunakan ABI non-standar, sebaris sepenuhnya, atau melakukan sejumlah optimasi lain yang mungkin tidak dapat dilakukan untuk fungsi dengan tautan eksternal.
@caf Apa yang Anda maksud dengan alamat fungsi diambil? Bagi saya, gagasan fungsi / variabel memiliki alamat atau ditugaskan alamat pada waktu kompilasi agak membingungkan. Bisakah Anda jelaskan?
SayeedHussain
2
@crypticcoder: Program Anda dimuat dalam memori, oleh karena itu fungsinya juga memiliki lokasi memori dan alamatnya dapat diperoleh. Dengan penunjuk fungsi, Anda dapat memanggil semua itu. Jika Anda melakukannya, itu mengurangi daftar optimisasi yang dapat dilakukan oleh kompiler karena kode harus tetap utuh di tempat yang sama.
5
@crypticcoder: Maksud saya ekspresi mengevaluasi pointer ke fungsi dan melakukan sesuatu dengannya selain segera memanggil fungsi. Jika sebuah penunjuk ke suatu staticfungsi keluar dari unit terjemahan saat ini, maka fungsi tersebut dapat langsung dipanggil dari unit terjemahan lainnya.
caf
@caf jika alamat fungsi diambil, akankah kompiler mendeteksi itu dan mematikan optimasi fungsi statis yang disebutkan dalam jawaban ini (misalnya menggunakan ABI non-standar)? Saya kira itu harus.
sevko
28
Itu static kunci dalam C digunakan dalam file yang dikompilasi (.c sebagai lawan dari .h) sehingga fungsi hanya ada di file itu.
Biasanya, ketika Anda membuat suatu fungsi, kompiler menghasilkan cruft yang bisa digunakan oleh linker, yah, menautkan pemanggilan fungsi ke fungsi itu. Jika Anda menggunakan kata kunci statis, fungsi lain dalam file yang sama dapat memanggil fungsi ini (karena dapat dilakukan tanpa beralih ke linker), sementara linker tidak memiliki informasi membiarkan file lain mengakses fungsi.
3Doub: Penggunaan kata "cruft" lebih tepat daripada yang Anda berikan kredit. Dalam konteks pertanyaan, "cruft" adalah kata yang tepat untuk digunakan di sini.
Erik Aronesty
@ 3Doubloons Saya setuju bahwa ini disederhanakan, tapi saya pikir itu membuatnya lebih mudah dipahami bagi pemula.
Ingo Bürk
11
Melihat posting di atas saya ingin menunjukkan satu detail.
Misalkan file utama kami ("main.c") terlihat seperti ini:
Pemrogram C menggunakan atribut statis untuk menyembunyikan deklarasi variabel dan fungsi di dalam modul, seperti halnya Anda akan menggunakan deklarasi publik dan pribadi di Java dan C ++. File sumber C memainkan peran modul. Setiap variabel global atau fungsi yang dideklarasikan dengan atribut statis adalah pribadi untuk modul itu. Demikian pula, setiap variabel atau fungsi global yang dideklarasikan tanpa atribut statis bersifat publik dan dapat diakses oleh modul lainnya. Merupakan praktik pemrograman yang baik untuk melindungi variabel dan fungsi Anda dengan atribut statis sedapat mungkin.
jawaban pmg sangat meyakinkan. Jika Anda ingin tahu bagaimana deklarasi statis bekerja pada level objek maka info di bawah ini bisa menarik bagi Anda. Saya menggunakan kembali program yang sama yang ditulis oleh pmg dan mengompilasinya menjadi file .so (shared object)
Konten berikut adalah setelah membuang file .so ke sesuatu yang dapat dibaca manusia
0000000000000675 f1 : alamat fungsi f1
000000000000068c f2 : alamat fungsi f2 (staticc)
perhatikan perbedaan dalam alamat fungsi, itu berarti sesuatu. Untuk fungsi yang dideklarasikan dengan alamat yang berbeda, ia dapat dengan baik menandakan bahwa f2 tinggal sangat jauh atau dalam segmen berbeda dari file objek.
Linker menggunakan sesuatu yang disebut PLT (Tabel tautan prosedur) dan GOT (tabel offset global) untuk memahami simbol yang mereka miliki aksesnya.
Untuk saat ini berpikir bahwa GOT dan PLT secara ajaib mengikat semua alamat dan bagian dinamis menyimpan informasi dari semua fungsi ini yang dapat dilihat oleh tautan.
Setelah membuang bagian dinamis dari file .so kita mendapatkan banyak entri tetapi hanya tertarik pada fungsi f1 dan f2 .
Bagian dinamis menyimpan entri hanya untuk fungsi f1 di alamat 0000000000000675 dan bukan untuk f2 !
Num: Nilai Ukuran Tipe Bind Vis Ndx Name
9:000000000000067523 FUNC GLOBAL DEFAULT 11 f1
Dan itu dia! Dari sini jelas bahwa penghubung akan gagal menemukan fungsi f2 karena tidak ada di bagian dinamis dari file .so.
Ketika ada kebutuhan untuk membatasi akses ke beberapa fungsi, kami akan menggunakan kata kunci statis sambil mendefinisikan dan mendeklarasikan suatu fungsi.
/* file ab.c */staticvoid function1(void){
puts("function1 called");}And store the following code in another file ab1.c
/* file ab1.c */int main(void){
function1();
getchar();return0;}/* in this code, we'll get a "Undefined reference to function1".Because function 1 is declared static in file ab.c and can't be used in ab1.c */
Jawaban:
Membuat fungsi
static
menyembunyikannya dari unit terjemahan lain, yang membantu menyediakan enkapsulasi .helper_file.c
main.c :
sumber
#include <helper_file.c>
? Saya pikir itu akan menjadikannya unit terjemahan tunggal ...gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c
. Prototipe untuk fungsi hadir di kedua file sumber (tidak perlu untuk file header). Tautan akan menyelesaikan fungsi.pmg tepat tentang enkapsulasi; selain menyembunyikan fungsi dari unit terjemahan lain (atau lebih tepatnya, karena itu), membuat fungsi
static
juga dapat memberi manfaat kinerja dengan adanya optimisasi kompiler.Karena suatu
static
fungsi tidak dapat dipanggil dari mana saja di luar unit terjemahan saat ini (kecuali jika kode membawa pointer ke alamatnya), kompiler mengontrol semua titik panggilan ke dalamnya.Ini berarti bahwa bebas untuk menggunakan ABI non-standar, sebaris sepenuhnya, atau melakukan sejumlah optimasi lain yang mungkin tidak dapat dilakukan untuk fungsi dengan tautan eksternal.
sumber
static
fungsi keluar dari unit terjemahan saat ini, maka fungsi tersebut dapat langsung dipanggil dari unit terjemahan lainnya.Itu
static
kunci dalam C digunakan dalam file yang dikompilasi (.c sebagai lawan dari .h) sehingga fungsi hanya ada di file itu.Biasanya, ketika Anda membuat suatu fungsi, kompiler menghasilkan cruft yang bisa digunakan oleh linker, yah, menautkan pemanggilan fungsi ke fungsi itu. Jika Anda menggunakan kata kunci statis, fungsi lain dalam file yang sama dapat memanggil fungsi ini (karena dapat dilakukan tanpa beralih ke linker), sementara linker tidak memiliki informasi membiarkan file lain mengakses fungsi.
sumber
Melihat posting di atas saya ingin menunjukkan satu detail.
Misalkan file utama kami ("main.c") terlihat seperti ini:
Sekarang pertimbangkan tiga kasus:
Kasus 1: File header kami ("header.h") terlihat seperti ini:
Kemudian perintah berikut di linux:
akan berhasil ! Mengikuti itu jika satu berjalan
Outputnya adalah
Fungsi panggilan di dalam header
Apa fungsi statis yang harus dicetak.
Kasus 2: File header kami ("header.h") terlihat seperti ini:
dan kami juga memiliki satu file lagi "header.c", yang terlihat seperti ini:
Lalu perintah berikut
akan memberikan kesalahan.
Kasus 3:
Mirip dengan kasus 2, kecuali bahwa sekarang file header kami ("header.h") adalah:
Maka perintah yang sama seperti dalam kasus 2 akan berhasil, dan eksekusi selanjutnya ./main akan memberikan hasil yang diharapkan.
Jadi dari tes ini (dijalankan pada mesin Acer x86, Ubuntu OS) saya membuat asumsi itu
kata kunci statis mencegah fungsi dipanggil dalam file * .c lain daripada yang didefinisikan.
Koreksi saya jika saya salah.
sumber
Pemrogram C menggunakan atribut statis untuk menyembunyikan deklarasi variabel dan fungsi di dalam modul, seperti halnya Anda akan menggunakan deklarasi publik dan pribadi di Java dan C ++. File sumber C memainkan peran modul. Setiap variabel global atau fungsi yang dideklarasikan dengan atribut statis adalah pribadi untuk modul itu. Demikian pula, setiap variabel atau fungsi global yang dideklarasikan tanpa atribut statis bersifat publik dan dapat diakses oleh modul lainnya. Merupakan praktik pemrograman yang baik untuk melindungi variabel dan fungsi Anda dengan atribut statis sedapat mungkin.
sumber
jawaban pmg sangat meyakinkan. Jika Anda ingin tahu bagaimana deklarasi statis bekerja pada level objek maka info di bawah ini bisa menarik bagi Anda. Saya menggunakan kembali program yang sama yang ditulis oleh pmg dan mengompilasinya menjadi file .so (shared object)
Konten berikut adalah setelah membuang file .so ke sesuatu yang dapat dibaca manusia
0000000000000675 f1 : alamat fungsi f1
000000000000068c f2 : alamat fungsi f2 (staticc)
perhatikan perbedaan dalam alamat fungsi, itu berarti sesuatu. Untuk fungsi yang dideklarasikan dengan alamat yang berbeda, ia dapat dengan baik menandakan bahwa f2 tinggal sangat jauh atau dalam segmen berbeda dari file objek.
Linker menggunakan sesuatu yang disebut PLT (Tabel tautan prosedur) dan GOT (tabel offset global) untuk memahami simbol yang mereka miliki aksesnya.
Untuk saat ini berpikir bahwa GOT dan PLT secara ajaib mengikat semua alamat dan bagian dinamis menyimpan informasi dari semua fungsi ini yang dapat dilihat oleh tautan.
Setelah membuang bagian dinamis dari file .so kita mendapatkan banyak entri tetapi hanya tertarik pada fungsi f1 dan f2 .
Bagian dinamis menyimpan entri hanya untuk fungsi f1 di alamat 0000000000000675 dan bukan untuk f2 !
Num: Nilai Ukuran Tipe Bind Vis Ndx Name
Dan itu dia! Dari sini jelas bahwa penghubung akan gagal menemukan fungsi f2 karena tidak ada di bagian dinamis dari file .so.
sumber
Ketika ada kebutuhan untuk membatasi akses ke beberapa fungsi, kami akan menggunakan kata kunci statis sambil mendefinisikan dan mendeklarasikan suatu fungsi.
sumber