Minimum çalıştırılabilir çok dosyalı kapsam örneği
Burada static
, birden çok dosyadaki işlev tanımlarının kapsamını nasıl etkilediğini göstereceğim.
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 akış yukarı .
Derleyin ve çalıştırın:
gcc -c a.c -o a.o
gcc -c main.c -o main.o
gcc -o main main.o a.o
./main
Çıktı:
main f
main sf
main f
a sf
yorumlama
sf
her dosya için bir tane olmak üzere iki ayrı fonksiyon vardır
- tek bir paylaşılan işlev var
f
Her zamanki gibi, kapsam ne kadar küçükse, o kadar iyidir, bu yüzden her zaman static
mümkünse işlevleri beyan edin.
C programlamasında, dosyalar genellikle "sınıfları" temsil etmek için kullanılır ve static
işlevler sınıfın "özel" yöntemlerini temsil eder.
Ortak bir C paterni, this
temel olarak C ++ 'ın başlık altında yaptığı ilk "yöntem" argümanı olarak bir yapı geçirmektir .
Standartlar bunun hakkında ne diyor
C99 N1256 taslak 6.7.1 "Depolama sınıfı belirteçleri" static
, bunun "depolama sınıfı belirleyicisi" olduğunu söylüyor .
6.2.2 / 3 "tanımlayıcıları Linkages" diyor static
ima internal linkage
:
Bir nesne veya işlev için dosya kapsamı tanımlayıcısının bildirimi, depolama sınıfı belirteci statik içeriyorsa, tanımlayıcı iç bağlantıya sahiptir.
ve 6.2.2 / 2 internal linkage
örneğimizdeki gibi davrandığını söylüyor :
Bir programın tamamını oluşturan çeviri birimleri ve kitaplıklar kümesinde, harici bağlantıya sahip belirli bir tanımlayıcının her bildirimi aynı nesneyi veya işlevi gösterir. Bir çeviri biriminde, iç bağlantıya sahip bir tanımlayıcının her bildirimi aynı nesneyi veya işlevi gösterir.
burada "çeviri birimi", önişlemeden sonra bir kaynak dosyasıdır.
GCC bunu ELF (Linux) için nasıl uygular?
İle STB_LOCAL
bağlanması.
Derlersek:
int f() { return 0; }
static int sf() { return 0; }
ve sembol tablosunu şunlarla sökün:
readelf -s main.o
çıktı şunları içerir:
Num: Value Size Type Bind Vis Ndx Name
5: 000000000000000b 11 FUNC LOCAL DEFAULT 1 sf
9: 0000000000000000 11 FUNC GLOBAL DEFAULT 1 f
yani aralarındaki tek önemli fark bağlanmadır. Value
yalnızca .bss
bölüme kaydırıldıklarından farklı olmasını bekliyoruz.
STB_LOCAL
http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html adresindeki ELF spesifikasyonunda belgelenmiştir :
STB_LOCAL Yerel semboller, tanımlarını içeren nesne dosyasının dışında görünmez. Aynı ada sahip yerel semboller, birbirlerini etkilemeden birden fazla dosyada bulunabilir
temsil etmek için mükemmel bir seçimdir static
.
Statik olmayan fonksiyonlar STB_GLOBAL
ve spesifikasyon şöyle diyor:
Bağlantı düzenleyicisi birkaç yeniden yerleştirilebilir nesne dosyasını birleştirdiğinde, aynı ada sahip birden fazla STB_GLOBAL sembol tanımına izin vermez.
birden fazla statik olmayan tanımdaki bağlantı hataları ile uyumludur.
Optimizasyonu ile kranklarsak -O3
, sf
sembol tamamen sembol tablosundan kaldırılır: yine de dışarıdan kullanılamaz. TODO, optimizasyon olmadığında neden statik fonksiyonları sembol tablosunda tutsun? Herhangi bir şey için kullanılabilirler mi?
Ayrıca bakınız
C ++ anonim ad alanları
C ++ 'da, statik yerine anonim ad alanlarını kullanmak isteyebilirsiniz, bu da benzer bir etki yaratır, ancak tür tanımlarını daha da gizler: Adsız / anonim ad alanları ve statik işlevler