C'de statik fonksiyon


172

C'de bir işlevi statik hale getirmenin anlamı nedir?


7
@nightcracker: C ++ 'da "yöntemler" diye bir şey yoktur. Bence Objective-C ile kafan karıştı.
Bo Persson

1
Hayır, Python ile kafam karıştı. Sınıf içindeki bir işleve Python'da yöntem denir.
orlp

Yanıtlar:


212

Bir işlev yapmak staticonu 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;
}

8
Çeviri birimi burada kullanılacak doğru terminoloji mi? Nesne dosyası daha doğru olmaz mıydı? Anladığım kadarıyla, statik bir işlev bağlayıcıdan gizlenir ve bağlayıcı çeviri birimlerinde çalışmaz.
Steven Eckhoff

2
Şunu da söylemeliydim ki, bunu bağlayıcıdan gizlenmiş olarak düşünmeyi seviyorum; bu şekilde daha net görünüyor.
Steven Eckhoff

1
yani, iç işlev (c dosyasının dışında çağırmamamız gerektiğinden emin olun), statik işlev olarak koymalıyız, değil mi? Yani, başka bir yerde arayamayacağından emin olabiliriz. Teşekkürler :)
hqt

1
Bunu nasıl derliyorsunuz? Kullanıyor musunuz #include <helper_file.c>? Sanırım bunu tek bir çeviri birimi yapar ...
Atcold

2
@Atcold: kodu yazma şeklim, komut satırına 2 kaynak dosyasını eklediğiniz gibi, bu şekilde 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

80

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


9
... işlevin adresi alınmadıkça.
caf

1
@caf Fonksiyonun adresi alınarak ne demek istiyorsun? Bana göre, adresleri olan veya derleme zamanında adres atanan işlevler / değişken kavramı biraz kafa karıştırıcıdır. Lütfen biraz açıklayabilir misiniz?
SayeedHussain

2
@crypticcoder: Programınız belleğe yüklenmiştir, bu nedenle işlevlerin de bir bellek konumu vardır ve adres alınabilir. Bir işlev işaretçisi ile bunlardan herhangi birini arayabilirsiniz. Bunu yaparsanız, kod aynı yerde bozulmadan kalması gerektiğinden derleyicinin gerçekleştirebileceği optimizasyonların listesini azaltır.

5
@crypticcoder: Bir ifadenin işleve bir işaretçiyi değerlendirdiği ve işlevle hemen çağrılmasından başka bir şey yaptığı anlamına gelir. Bir staticişleve işaretçi geçerli çeviri biriminden kaçarsa, bu işlev doğrudan diğer çeviri birimlerinden çağrılabilir.
caf

@caf işlevin adresi alınırsa, derleyici bunu algılar ve bu yanıtta belirtilen statik işlev optimizasyonlarını kapatır mı (örneğin standart olmayan bir ABI kullanarak)? Sanırım öyle olurdu.
sevko

28

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


1
3Doub: "Cruft" kelimesinin kullanımı, ona verdiğiniz krediden daha kesindir. Soru bağlamında, "ham" burada kullanılacak doğru kelimedir.
Erik Aronesty

@ 3Doubloons Basitleştirilmiş olduğunu kabul ediyorum, ama bunun yeni başlayanlar için anlaşılmasını çok daha kolay hale getirdiğini düşünüyorum.
Ingo Bürk

11

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.


5

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.


4

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 .


0

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

Bu cevap çok yardımcı değil.
fiscblog
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.