Contoh lingkup multi-file runnable minimal
Di sini saya menggambarkan bagaimana static
memengaruhi ruang lingkup definisi fungsi di beberapa file.
ac
#include <stdio.h>
/* Undefined behavior: already defined in main.
* Binutils 2.24 gives an error and refuses to link.
* /programming/27667277/why-does-borland-compile-with-multiple-definitions-of-same-object-in-different-c
*/
/*void f() { puts("a f"); }*/
/* OK: only declared, not defined. Will use the one in main. */
void f(void);
/* OK: only visible to this file. */
static void sf() { puts("a sf"); }
void a() {
f();
sf();
}
main.c
#include <stdio.h>
void a(void);
void f() { puts("main f"); }
static void sf() { puts("main sf"); }
void m() {
f();
sf();
}
int main() {
m();
a();
return 0;
}
GitHub hulu .
Kompilasi dan jalankan:
gcc -c a.c -o a.o
gcc -c main.c -o main.o
gcc -o main main.o a.o
./main
Keluaran:
main f
main sf
main f
a sf
Penafsiran
- ada dua fungsi terpisah
sf
, satu untuk setiap file
- ada satu fungsi bersama
f
Seperti biasa, semakin kecil cakupannya, semakin baik, jadi selalu nyatakan fungsi static
jika Anda bisa.
Dalam pemrograman C, file sering digunakan untuk mewakili "kelas", dan static
fungsi mewakili metode "pribadi" kelas.
Pola C yang umum adalah meneruskan this
struct sekitar sebagai argumen "metode" pertama, yang pada dasarnya adalah apa yang dilakukan C ++ di bawah tenda.
Apa standar katakan tentang itu
C99 N1256 konsep 6.7.1 " Penentu kelas penyimpanan" mengatakan bahwa itu static
adalah " penentu kelas penyimpanan".
6.2.2 / 3 "Tautan pengidentifikasi" kata static
menyiratkan internal linkage
:
Jika deklarasi pengidentifikasi lingkup file untuk objek atau fungsi berisi statis specifier kelas penyimpanan, pengidentifikasi memiliki tautan internal.
dan 6.2.2 / 2 mengatakan bahwa internal linkage
berperilaku seperti pada contoh kita:
Dalam himpunan unit terjemahan dan pustaka yang merupakan keseluruhan program, setiap deklarasi pengidentifikasi tertentu dengan tautan eksternal menunjukkan objek atau fungsi yang sama. Dalam satu unit terjemahan, setiap deklarasi pengidentifikasi dengan tautan internal menunjukkan objek atau fungsi yang sama.
di mana "unit terjemahan" adalah file sumber setelah preprocessing.
Bagaimana GCC mengimplementasikannya untuk ELF (Linux)?
Dengan STB_LOCAL
mengikat.
Jika kami kompilasi:
int f() { return 0; }
static int sf() { return 0; }
dan bongkar tabel simbol dengan:
readelf -s main.o
output berisi:
Num: Value Size Type Bind Vis Ndx Name
5: 000000000000000b 11 FUNC LOCAL DEFAULT 1 sf
9: 0000000000000000 11 FUNC GLOBAL DEFAULT 1 f
jadi pengikatan adalah satu-satunya perbedaan yang signifikan di antara mereka. Value
hanya offset mereka ke dalam .bss
bagian, jadi kami perkirakan berbeda.
STB_LOCAL
didokumentasikan pada spesifikasi ELF di http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html :
STB_LOCAL Simbol lokal tidak terlihat di luar file objek yang berisi definisi mereka. Simbol lokal dengan nama yang sama mungkin ada dalam beberapa file tanpa mengganggu satu sama lain
yang membuatnya menjadi pilihan yang tepat untuk diwakili static
.
Fungsi tanpa statis adalah STB_GLOBAL
, dan spesifikasi mengatakan:
Ketika editor tautan menggabungkan beberapa file objek yang dapat dipindahkan, itu tidak memungkinkan beberapa definisi simbol STB_GLOBAL dengan nama yang sama.
yang koheren dengan kesalahan tautan pada beberapa definisi non statis.
Jika kita menghidupkan optimasi dengan -O3
, sf
simbol dihapus seluruhnya dari tabel simbol: itu tidak dapat digunakan dari luar. TODO mengapa menyimpan fungsi statis pada tabel simbol sama sekali ketika tidak ada optimasi? Bisakah mereka digunakan untuk apa saja?
Lihat juga
C ++ ruang nama anonim
Dalam C ++, Anda mungkin ingin menggunakan ruang nama anonim alih-alih statis, yang mencapai efek yang serupa, tetapi lebih lanjut menyembunyikan definisi tipe: Ruang nama tidak bernama / anonim vs. fungsi statis