C'de bir işlevi statik hale getirmenin anlamı nedir?
C'de bir işlevi statik hale getirmenin anlamı nedir?
Yanıtlar:
Bir işlev yapmak static
onu diğer çeviri birimlerinden gizler, bu da kapsüllemenin sağlanmasına yardımcı olur .
helper_file.c
int f1(int); /* prototype */
static int 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) {
return 42 + 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 */
return 0;
}
#include <helper_file.c>
? Sanırım bunu tek bir çeviri birimi yapar ...
gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c
. İşlevlerin prototipleri her iki kaynak dosyasında da bulunur (başlık dosyalarına gerek yoktur). Bağlayıcı işlevleri çözecektir.
pmg , kapsülleme ile ilgili noktadır; işlevi diğer çeviri birimlerinden gizlemenin ötesinde (ya da daha doğrusu, çünkü )static
derleyici optimizasyonlarının varlığında performans avantajları da sağlayabilir.
Bir static
işlev geçerli çeviri biriminin dışındaki herhangi bir yerden çağırılamadığından (kod adresine bir işaretçi almadığı sürece), derleyici tüm çağrı noktalarını kontrol eder.
Bu, standart olmayan bir ABI kullanmak, tamamen satır içi kullanmak veya harici bağlantıya sahip bir işlev için mümkün olmayan diğer optimizasyonları yapmakta özgür olduğu anlamına gelir.
static
işleve işaretçi geçerli çeviri biriminden kaçarsa, bu işlev doğrudan diğer çeviri birimlerinden çağrılabilir.
static
C anahtar derlenmiş dosya (.h karşı .c), böylece fonksiyon sadece bu dosya varolduğunu kullanılır.
Normalde, bir işlev oluşturduğunuzda, derleyici bağlayıcının bu işleve bir işlev çağrısını bağlamak için kullanabileceği bir hamle üretir. Statik anahtar sözcüğü kullanırsanız, aynı dosyadaki diğer işlevler bu işlevi çağırabilir (çünkü bağlayıcıya başvurmadan yapılabilir), ancak bağlayıcıda diğer dosyaların işleve erişmesine izin veren hiçbir bilgi yoktur.
Yukarıdaki yazılara baktığımda bir ayrıntıya dikkat çekmek istiyorum.
Ana dosyamızın ("main.c") şöyle olduğunu varsayalım:
#include "header.h"
int main(void) {
FunctionInHeader();
}
Şimdi üç vakayı düşünün:
Durum 1: Başlık dosyamız ("header.h") şöyle görünür:
#include <stdio.h>
static void FunctionInHeader();
void FunctionInHeader() {
printf("Calling function inside header\n");
}
Sonra linux üzerinde aşağıdaki komut:
gcc main.c header.h -o main
başarılı olacak ! Eğer biri koşarsa
./main
Çıktı
Başlık içindeki çağrı fonksiyonu
Bu statik fonksiyonun yazdırması gereken şeydir.
Durum 2: Başlık dosyamız ("header.h") şöyle görünür:
static void FunctionInHeader();
ayrıca şöyle bir dosya daha var "header.c", şuna benzer:
#include <stdio.h>
#include "header.h"
void FunctionInHeader() {
printf("Calling function inside header\n");
}
Sonra aşağıdaki komut
gcc main.c header.h header.c -o main
bir hata verecektir.
Durum 3:
Durum 2'ye benzer, ancak artık başlık dosyamız ("header.h"):
void FunctionInHeader(); // keyword static removed
Sonra durum 2'deki komutla aynıdır ve ./main komutunun daha fazla yürütülmesi beklenen sonucu verecektir.
Bu testlerden (Acer x86 makinesinde, Ubuntu OS'de yürütülür),
statik anahtar kelime, işlevin tanımlandığı yerden başka bir * .c dosyasında çağrılmasını engeller.
Eğer Yanlışsam beni düzelt.
C programcıları, Java ve C ++ 'da genel ve özel bildirimleri kullandığınız gibi, modül içindeki değişken ve işlev bildirimlerini gizlemek için statik niteliği kullanır. C kaynak dosyaları modüllerin rolünü oynar. Statik öznitelik ile bildirilen herhangi bir genel değişken veya işlev o modüle özeldir. Benzer şekilde, statik öznitelik olmadan bildirilen herhangi bir genel değişken veya işlev herkese açıktır ve başka herhangi bir modül tarafından erişilebilir. Değişkenlerinizi ve fonksiyonlarınızı mümkün olan her yerde statik özellik ile korumak iyi bir programlama uygulamasıdır.
pmg'nin cevabı çok inandırıcı. Statik bildirimlerin nesne düzeyinde nasıl çalıştığını bilmek istiyorsanız, aşağıdaki bilgiler sizin için ilginç olabilir. Pmg tarafından yazılmış aynı programı tekrar kullandım ve .so (paylaşılan nesne) dosyasına derledim
Aşağıdaki içerik .so dosyasını insan tarafından okunabilir bir şeye döktükten sonradır
0000000000000675 f1 : f1 işlevinin adresi
000000000000068c f2 : f2 (staticc) işlevinin adresi
fonksiyon adresindeki farkı not edin, bir şey ifade eder. Farklı adresle bildirilen bir işlev için, f2'nin çok uzakta veya nesne dosyasının farklı bir kesiminde yaşadığını çok iyi gösterebilir.
Bağlayıcılar, bağlantıya erişimi olan sembolleri anlamak için PLT (Yordam bağlantı tablosu) ve GOT (Global ofset tablosu) adlı bir şey kullanır.
Şimdilik, GOT ve PLT'nin tüm adresleri sihirli bir şekilde bağladığını ve dinamik bir bölümün bağlayıcı tarafından görülebilen tüm bu işlevlerin bilgilerini içerdiğini düşünün.
.So dosyasının dinamik bölümünü döktükten sonra bir sürü girdi alırız, ancak yalnızca f1 ve f2 işleviyle ilgileniriz .
Dinamik bölüm f2 işlevi için değil, 0000000000000675 adresinde yalnızca f1 işlevi için girişi tutar !
Num: Değer Boyut Tip Bağlama Vis Ndx Adı
9: 0000000000000675 23 FUNC GLOBAL DEFAULT 11 f1
Ve bu kadar !. Bundan, bağlayıcının .2 dosyasının dinamik bölümünde olmadığı için f2 işlevini bulmada başarısız olacağı açıktır .
Bazı işlevlere erişimi kısıtlamaya ihtiyaç duyulduğunda, bir işlevi tanımlarken ve bildirirken statik anahtar sözcüğü kullanırız.
/* file ab.c */
static void function1(void)
{
puts("function1 called");
}
And store the following code in another file ab1.c
/* file ab1.c */
int main(void)
{
function1();
getchar();
return 0;
}
/* 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 */