C'de “statik” fonksiyon nedir?


506

Soru düz hakkındaydı fonksiyonlar değil static yorumlarda açıklandığı gibi yöntemler.

staticDeğişkenin ne olduğunu anlıyorum , ama staticfonksiyon nedir?

Ve neden bir işlev beyan edersem, diyelim ki void print_matrix, a.c(OLMADAN a.h) diyelim ve içerelim "a.c"- anladım "print_matrix@@....) already defined in a.obj", ama eğer static void print_matrixderler gibi derlerse?

GÜNCELLEME Sadece bir şeyleri temizlemek için - .cçoğunuzun işaret ettiği gibi dahil olmanın kötü olduğunu biliyorum . main.cTüm bu işlevlerin uygun .hve .cdosyalara nasıl gruplanacağı konusunda daha iyi bir fikre sahip olana kadar alanı geçici olarak temizlemek için yapıyorum . Sadece geçici, hızlı bir çözüm.

Yanıtlar:


685

staticişlevler, yalnızca aynı dosyadaki diğer işlevler tarafından görülebilir işlevlerdir (daha kesin olarak aynı çeviri birimi ).

DÜZENLEME : Düşünenlere göre, soruların yazarı bir 'sınıf yöntemi' anlamına geliyordu: Soru etiketlendiğinden, Ceski bir C fonksiyonu anlamına geliyor. (C ++ / Java / ...) sınıfı yöntemleri için, staticbu yöntemin sınıfın kendisinde çağrılabileceği anlamına gelir, bu sınıfın bir örneği gerekmez.


2
Aslında c ++ etiketlemedim, bazı yöneticiler muhtemelen yaptı, ama C ++ ile ilgili, bu yüzden C ++ farkı nedir?
Slava V

16
C ++ yöntemleri genellikle "üye işlevleri" olarak adlandırılır, bu nedenle C ++ biraz belirsizliği katılıyorum. Bu senin hatan değil - dil sadece iki farklı şey için anahtar kelimeyi kullanıyor.
Chuck

2
Hayır, hala C ++ işlevi anlamına geliyor. C ++ üye işlevi yerine C ++ serbest işlevi.
Orbit'te Hafiflik Yarışları

3
@Chuck: C ++ terminolojisi asla "method" kelimesini kullanmaz; Bu Java terminolojisidir - C ++ standart belgelerinde her zaman "üye işlevi" olarak adlandırılır ( bu cevaba veya C ++ ile Java terimleri sözlüğüne bakın (örn. C ++ "veri üyesi" kullanır ve Java "alan" vb. kullanır)).
ShreevatsaR

6
Bu cevabı biraz açıklığa kavuşturmak için: işlevin adı , aynı çeviri biriminin diğer bölümleri tarafından, o adın ilk bildiriminin altında görünür. Bu fonksiyon diğer birimlerden (ve aynı birimin önceki bölümlerinden) başka yollarla, örneğin bir fonksiyon işaretçisi ile çağrılabilir.
MM

199

C'deki statik işlevler ile C ++ 'daki statik üye işlevleri arasında büyük bir fark vardır. C'de statik bir işlev, derlendiği nesne dosyası olan çeviri biriminin dışında görünmez. Başka bir deyişle, bir işlevi statik yapmak kapsamını sınırlar. Statik bir işlevi * .c dosyasına "özel" olarak düşünebilirsiniz (bu kesinlikle doğru olmasa da).

C ++ 'da, "statik" üye işlevlerine ve sınıfların veri üyelerine de uygulanabilir. Statik olmayan bir veri üyesine "sınıf değişkeni" de denirken, statik olmayan bir veri üyesine "örnek değişkeni" denir. Bu Smalltalk terminolojisidir. Bu, bir sınıfın tüm nesneleri tarafından paylaşılan bir statik veri üyesinin yalnızca bir kopyası olduğu anlamına gelirken, her nesnenin statik olmayan bir veri üyesinin kendi kopyası vardır. Bu nedenle, statik veri üyesi temelde küresel bir değişkendir, yani bir sınıfın üyesidir.

Statik olmayan üye fonksiyonları, sınıfın tüm veri üyelerine erişebilir: statik ve statik olmayan. Statik üye işlevleri yalnızca statik veri üyeleri üzerinde çalışabilir.

Bunu düşünmenin bir yolu, C ++ statik veri üyelerinin ve statik üye işlevlerinin herhangi bir nesneye değil, tüm sınıfa ait olmasıdır.


42
C ++ 'da dosya statik vardır. C'yi buna getirmeye gerek yok.
Orbit'te Hafiflik Yarışları

17
C ++ 'da, statik bir işlev statik bir işlevdir. Statik üye işlevi, yöntem olarak da bilinen statik üye işlevidir. C'nin üye olmaması, işlevlerin "C" olduğu anlamına gelmez.
Gerasimos R

3
global var ve class static var arasında (ad alanı hariç) herhangi bir fark var mı?
Alexander Malakhov

3
Ad alanı ana farktır. Diğer fark, statik veri üyesini özel yapabilmeniz ve böylece yalnızca sınıfın üye işlevlerinden erişilebilmenizdir. Başka bir deyişle, statik değişkene genel değişkenle karşılaştırıldığında çok daha fazla denetiminiz vardır.
Dima

2
Birisi statik bir işlevi kendi .c dosyasına özel olarak düşünmenin neden doğru olmadığını açıklayabilir mi? Söylenecek ne kaldı?
YoTengoUnLCD

77

C ++ 'da işlevler söz konusu olduğunda, static anahtar sözcüğünün iki kullanımı vardır.

Birincisi, diğer çeviri birimlerinde referans gösterilememesi için işlevi dahili bağlantıya sahip olarak işaretlemektir. Bu kullanım C ++ 'da kullanımdan kaldırılmıştır. Bu kullanım için adsız ad alanları tercih edilir.

// inside some .cpp file:

static void foo();    // old "C" way of having internal linkage

// C++ way:
namespace
{
   void this_function_has_internal_linkage()
   {
      // ...
   }
}

İkinci kullanım bir sınıf bağlamındadır. Bir sınıfın statik üye işlevi varsa, bu işlev sınıfın bir üyesi olduğu (ve diğer üyelere olağan erişime sahip olduğu) anlamına gelir, ancak belirli bir nesne aracılığıyla çağrılması gerekmez. Başka bir deyişle, bu işlevin içinde "bu" işaretçi yoktur.


1
Soru c.
Deqing

8
@ Soruyu çözmek aslında C ++ olarak etiketlendi ve yazar ".cpp" dosyalarını kullanmaktan bahsediyor.
Brian Neal

57

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

  • sfher 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 staticmümkünse işlevleri beyan edin.

C programlamasında, dosyalar genellikle "sınıfları" temsil etmek için kullanılır ve staticişlevler sınıfın "özel" yöntemlerini temsil eder.

Ortak bir C paterni, thistemel 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 staticima 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_LOCALbağ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. Valueyalnızca .bssbölüme kaydırıldıklarından farklı olmasını bekliyoruz.

STB_LOCALhttp://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_GLOBALve 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, sfsembol 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


3
not: void f() { puts("sf"); }(yani iki tanımı f()), herhangi bir teşhis gerektirmeden tanımlanmamış davranışa neden olur. Aslında bir hata mesajı görmek bağlantı kalitesi sorunudur.
MM

2
Bu en iyi ve kesin açıklama! Senden sonra!
Aqua

20

Düz C işlevleri hakkında - C ++ sınıfında değiştiricinin başka bir anlamı vardır.

Yalnızca bir dosyanız varsa, bu değiştirici kesinlikle fark etmez. Fark, birden fazla dosya içeren daha büyük projelerde gelir:

C'de, her "modül" (sample.c ve sample.h kombinasyonu) bağımsız olarak derlenir ve daha sonra bu derlenmiş nesne dosyalarının her biri (sample.o) bağlayıcı tarafından yürütülebilir bir dosyaya bağlanır.

Diyelim ki ana dosyanıza eklediğiniz birkaç dosya var ve bunlardan ikisinin yalnızca dahili olarak kullanılan rahatlık için kullanılan bir işlevi var add(int a, b)- derleyici bu iki modül için kolayca nesne dosyaları oluşturacak, ancak bağlayıcı bir hata atacak, çünkü aynı ada sahip iki işlev bulur ve hangisini kullanması gerektiğini bilmez (bağlantı yapacak bir şey olmasa bile, çünkü başka bir yerde değil, kendi dosyasında kullanılırlar).

Bu nedenle, yalnızca dahili olarak kullanılan bu işlevi statik bir işlev haline getirirsiniz. Bu durumda derleyici, bağlayıcı için tipik "bu şeyi bağlayabilirsiniz" -flag oluşturmaz, böylece bağlayıcı bu işlevi görmez ve bir hata oluşturmaz.


16

Birincisi: Bir .cppdosyayı başka bir dosyaya eklemek genellikle kötü bir fikirdir - bunun gibi sorunlara yol açar :-) Normal yol, ayrı derleme birimleri oluşturmak ve dahil edilen dosya için bir başlık dosyası eklemektir.

İkincisi:

C ++ burada kafa karıştırıcı bir terminolojiye sahiptir - yorumlarda belirtilene kadar bunu bilmiyordum.

a) static functions- C'den miras alınır ve burada ne hakkında konuştuğunuz. Herhangi bir sınıfın dışında. Statik işlev , geçerli derleme biriminin dışında görünmediği anlamına gelir; bu nedenle a.obj dosyasının bir kopyası ve diğer kodunuzun bağımsız bir kopyası vardır. (Son yürütülebilir dosyayı kodun birden çok kopyasıyla şişirmek).

b) static member function- Ne Nesne Oryantasyon terimleri statik yöntemi . Bir sınıfın içinde yaşıyor. Bunu bir nesne örneği yerine sınıfla çağırırsınız.

Bu iki farklı statik fonksiyon tanımı tamamen farklıdır. Dikkatli ol - işte ejderhalar.


Peki, bunu uygun .hpp ile birlikte kütüphanelere nasıl organize edeceğime karar verene kadar main.cpp'deki TEMPORARILY adlı alanı temizlemek için yapıyorum. Bunun nasıl yapılacağı hakkında daha iyi bir fikir var mı?
Slava V

1
C ++ 'da doğru terminoloji yöntem değil üye işlevidir. C ++ legalese'de "yöntem" yoktur. Yöntem genel bir OO terimidir. C ++ bunları üye işlevleri aracılığıyla uygular.
Brian Neal

14

statik işlev tanımları bu sembolü dahili olarak işaretler. Bu yüzden dışarıdan bağlantı için görünmez, sadece aynı derleme birimindeki, genellikle aynı dosyadaki işlevlere görünür.


7

Statik işlev, sınıfın bir örneğinin aksine, sınıfın kendisinde çağrılabilen işlevdir.

Örneğin statik olmayan:

Person* tom = new Person();
tom->setName("Tom");

Bu yöntem, sınıfın kendisi üzerinde değil, sınıfın bir örneğinde çalışır. Ancak, bir örneğe sahip olmadan çalışabilecek statik bir yönteminiz olabilir. Bu bazen Fabrika modelinde kullanılır:

Person* tom = Person::createNewPerson();

2
Bana öyle geliyor ki, statik "yöntem" hakkında konuşuyorsunuz, "işlev" değil ??
Slava V

Bir sınıf içindeki statik fonksiyonlardan bahsettiğinizi sanıyordum.
Papağanlar

Eğer bilindim "yöntemleri" C ++ "yöntem fonksiyonları" denir, bu konuda daha açık olurdu. Peki, şimdi yapıyorum :) Yine de teşekkürler
Slava V

5
C ++ 'da "yöntem" yoktur, sadece işlevler. C ++ standardı "yöntemlerden" bahsetmez, sadece "işlevlerden" bahseder.
Brian Neal

1
@Puddle Ne dediğini biliyorum ama C ++ standardında bir "yöntem" tanımı yoktur. C ++ sadece çeşitli fonksiyonlara sahiptir. "Yöntem" genel bir OO terimidir ve diğer dillerde ve gayri resmi olarak C ++ 'da kullanılır. Bir yöntem resmi olarak C ++ 'da "üye işlevi" olarak bilinir.
Brian Neal

7

Küçük nit: statik işlevler, en pratik durumlar için işlevin tanımlandığı dosya olan bir çeviri birimi tarafından görülebilir. Aldığınız hataya genellikle Tek Tanımlama Kuralının ihlali denir.

Standart muhtemelen şöyle bir şey söylüyor:

"Her program, o programda kullanılan her satır içi olmayan fonksiyonun veya nesnenin tam olarak bir tanımını içermelidir; teşhis gerektirmez."

Statik fonksiyonlara bakmanın C yolu budur. Ancak bu C ++ 'da kullanımdan kaldırılmıştır.

C ++ 'da, üye işlevlerini statik olarak bildirebilirsiniz. Bunlar çoğunlukla metafonksiyonlardır, yani belirli bir nesnenin davranışını / durumunu tanımlamaz / değiştirmezler, ancak tüm sınıfın kendisi üzerinde hareket ederler. Ayrıca, bu, statik üye işlevini çağırmak için bir nesne oluşturmanız gerekmediği anlamına gelir. Ayrıca, bu aynı zamanda, sadece böyle bir fonksiyon içinden statik üye değişkenlere erişebileceğiniz anlamına gelir.

Parrot örneğine, bir programın ömrü boyunca tek bir nesneyi almak / kullanmak için statik bir üye işlevi bu tür dayalı Singleton desen eklemek istiyorum.


7

Statik işlevin cevabı dile bağlıdır:

1) C gibi OOPS bulunmayan dillerde, işlevin yalnızca tanımlandığı dosyadan erişilebilir olduğu anlamına gelir.

2) C ++ gibi OOPS içeren dillerde, işlevin bir örneği oluşturmadan doğrudan sınıfa çağrılabileceği anlamına gelir.


Bu doğru değil. İkinci paragrafınızın açıklaması, " statik işlevler " değil, bir sınıfın " statik üye işlevleri " anlamına gelir . C ++ 'da, C alanında olduğu gibi dosya kapsamına da sahip bir işlev vardırstatic
RobertS, Monica Cellio'yu destekler

0

Statik fonksiyon sadece bu dosyada görülebildiğinden. Aslında, bazı işlevlere "statik" bildirirseniz derleyici sizin için bazı optimizasyonlar yapabilir .

İşte basit bir örnek.

main.c

#include <stdio.h>

static void test() 
{
    ghost(); // This is an unexist function.
}

int main()
{
    int ret = 0;

#ifdef TEST
#else
    test();
#endif
    return (ret);
} 

Ve derleyin

gcc -o main main.c

Başarısız olduğunu göreceksiniz. Çünkü ghost () fonksiyonunu bile uygulamıyorsunuz.

Ama aşağıdaki komutu kullanırsak.

gcc -DTEST -O2 -o main main.c

O başarı ve bu program normal olarak yürütülebilir.

Neden? 3 kilit nokta vardır.

  1. -O2: Derleyici optimizasyon seviyesi en az 2.
  2. -DTEST: TEST'i tanımlayın, böylece test () çağrılmaz.
  3. Test etmek için "statik" tanımlandı ().

Sadece bu 3 koşulun hepsi doğruysa, derlemeyi geçebilirsiniz. Bu "statik" bildirimi nedeniyle, derleyici test () 'in başka bir dosyada ASLA çağrılmayacağını doğrulayabilir. Derleyiciniz derlerken testi () kaldırabilir. Test () işlevine ihtiyacımız olmadığından, ghost () öğesinin tanımlanması veya uygulanması önemli değildir.


0

" C'de " static"işlevi nedir? "

En baştan başlayalım.

Her şey "bağlantı" adı verilen bir şeye dayanmaktadır:

Msgstr "" " Aynı kapsamda veya aynı kapsamda bir kereden fazla bildirilen bir tanımlayıcı, bağlantı adı verilen bir işlemle aynı nesneye veya işleve atıfta bulunmak için yapılabilir. 29) Üç tür bağlantı vardır: harici, dahili ve hiçbiri. "

Kaynak: C18, 6.2.2 / 1


"Bir programın tamamını oluşturan çeviri birimleri ve kütüphaneler kümesinde, harici bağlantılı belirli bir tanımlayıcının her bildirimi aynı nesneyi veya işlevi belirtir. Bir çeviri biriminde, dahili bağlantılı bir tanımlayıcının her bildirimi aynı nesneyi veya işlevi gösterir. . Bağlantısız bir tanımlayıcının her bildirimi benzersiz bir varlığı ifade eder. "

Kaynak: C18, 6.2.2 / 2


Bir işlev depolama sınıfı belirticisi olmadan tanımlanırsa, işlevin externvarsayılan olarak al bağlantısı vardır :

"Bir işlev için bir tanımlayıcının bildiriminde depolama sınıfı belirteci yoksa, bağlantısı tam olarak depolama sınıfı belirtici harici ile bildirilmiş gibi belirlenir ."

Kaynak: C18, 6.2.2 / 5

Bu, - programınız birkaç çeviri birimi / kaynak dosyasından ( .cveya .cpp) içeriyorsa , işlevin programınızın sahip olduğu tüm çeviri birimlerinde / kaynak dosyalarında görülebileceği anlamına gelir .

Bu, bazı durumlarda bir sorun olabilir. Fe iki farklı işlev (tanım) kullanmak istiyorsanız, ancak iki farklı bağlamda (aslında dosya bağlamı) aynı işlev adına sahip olursanız ne olur?

C ve C ++ 'da, staticdosya kapsamındaki bir işleve (C ++' da bir sınıfın statik üye işlevi değil veya başka bir blok içindeki bir işleve) uygulanan depolama sınıfı niteleyicisi artık yardımcı olmaya gelir ve ilgili işlevin yalnızca diğer TLU / dosyalarda değil, tanımlandığı çeviri birimi / kaynak dosyası.

"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ı dahili bağlantıya sahiptir. 30)"


30) Bir işlev bildirimi, dosya kapsamındaysa, depolama sınıfı belirteçlerini içerebilir; bakınız 6.7.1.

Kaynak: C18, 6.2.2 / 3


Böylece, bir staticişlev sadece mantıklıdır, iff:

  1. Programınız birkaç çeviri biriminden / kaynak dosyasından ( .cveya.cpp ) oluşur.

    ve

  2. Bir işlevin kapsamını, içinde belirli bir işlevin tanımlandığı dosya ile sınırlamak istiyorsunuz.

Değilse hem bu gereksinimlerin maç, siz bir işlev sınırlandırılması konusundaki başınızın etrafında sarmak gerekmez static.


Yan Notlar:

  • Daha önce de belirtildiği gibi, A staticfonksiyonunun C ve C ++ arasında hiçbir farkı yoktur , çünkü bu C ++ 'dan miras alınan bir özelliktir.

    C ++ topluluğunda, adlandırılmamış ad alanlarınınstatic kullanımına kıyasla niteleyici işlevlerin amortismanı hakkında yürek parçalayıcı bir tartışma olması önemli değildir. , önce C ++ 03 standardında yanlış yerleştirilmiş bir paragrafla başlatılan ve kısa süre önce komitenin kendisi tarafından gözden geçirilen ve C ++ 11'de kaldırılan statik işlevler.

    Bu çeşitli SO sorularına tabiydi:

    Adsız / anonim ad alanları ve statik işlevler

    Adsız ad alanının statikten üstünlüğü?

    Adsız bir ad alanı neden statik için "üstün" bir alternatiftir?

    Statik anahtar kelimenin kullanımdan kaldırılması ... artık yok mu?

    Aslında, henüz C ++ standardına göre kullanımdan kaldırılmamıştır. Bu nedenle, staticişlevlerin kullanımı hala yasaldır. Adsız ad alanlarının avantajları olsa bile , C ++ 'da statik işlevlerin kullanılması ya da kullanılmaması ile ilgili tartışma kişinin zihnine (görüş tabanlı) ve bu web sitesi için uygun değildir.

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.