Apa fungsi "statis" dalam C?

506

Pertanyaannya tentang polos fungsi, bukan static metode, seperti yang dijelaskan dalam komentar.

Saya mengerti apa staticvariabel itu, tapi apa staticfungsinya?

Dan mengapa jika saya mendeklarasikan suatu fungsi, katakanlah void print_matrix, dalam katakanlah a.c(TANPA a.h) dan sertakan "a.c"- Saya mengerti "print_matrix@@....) already defined in a.obj", TETAPI jika saya mendeklarasikan fungsinya static void print_matrixsaat kompilasi?

PEMBARUAN Hanya untuk menjernihkan - saya tahu bahwa termasuk .citu buruk, seperti yang banyak dari Anda tunjukkan. Saya hanya melakukannya untuk membersihkan ruang sementara main.csampai saya memiliki ide yang lebih baik tentang bagaimana mengelompokkan semua fungsi tersebut ke dalam file .hdan .cfile yang benar. Hanya solusi sementara dan cepat.

Slava V
sumber

Jawaban:

685

staticfungsi adalah fungsi yang hanya dapat dilihat oleh fungsi lain dalam file yang sama (lebih tepatnya unit terjemahan yang sama ).

EDIT : Bagi mereka yang berpikir, bahwa penulis pertanyaan berarti 'metode kelas': Ketika pertanyaan itu ditandai Cdia berarti fungsi C tua yang sederhana. Untuk metode kelas (C ++ / Java / ...), staticberarti metode ini dapat dipanggil pada kelas itu sendiri, tidak perlu instance dari kelas itu.

Johannes Weiss
sumber
2
Sebenarnya saya tidak menandai c ++, beberapa admin mungkin melakukannya, tapi itu tentang C ++, jadi apa bedanya C ++?
Slava V
16
Metode C ++ sering disebut sebagai "fungsi anggota", jadi saya setuju bahwa C ++ memperkenalkan sedikit ambiguitas. Itu bukan salah Anda - bahasa hanya menggunakan kata kunci untuk dua hal yang berbeda.
Chuck
2
Tidak, dia masih berarti fungsi C ++. Fungsi bebas C ++ daripada fungsi anggota C ++.
Lightness Races dalam Orbit
3
@Cek: terminologi C ++ tidak pernah menggunakan kata "metode"; itulah terminologi Java - dalam dokumen standar C ++ selalu disebut "fungsi anggota" (lihat jawaban ini atau glosarium istilah C ++ vs Java (mis. C ++ menggunakan "data member" dan Java menggunakan "field", dll)).
ShreevatsaR
6
Untuk sedikit memperjelas jawaban ini: nama fungsi hanya dapat dilihat oleh bagian lain dari unit terjemahan yang sama, di bawah pernyataan pertama nama itu. Fungsi dapat dipanggil dari unit lain (dan bagian sebelumnya dari unit yang sama) melalui cara lain, misalnya penunjuk fungsi.
MM
199

Ada perbedaan besar antara fungsi statis di C dan fungsi anggota statis di C ++. Dalam C, fungsi statis tidak terlihat di luar unit terjemahannya, yang merupakan file objek yang dikompilasi. Dengan kata lain, membuat fungsi statis membatasi ruang lingkupnya. Anda dapat menganggap fungsi statis sebagai "pribadi" untuk file * .c-nya (walaupun itu tidak sepenuhnya benar).

Dalam C ++, "statis" juga dapat berlaku untuk fungsi anggota dan data anggota kelas. Anggota data statis juga disebut "variabel kelas", sedangkan anggota data non-statis adalah "variabel instan". Ini adalah terminologi Smalltalk. Ini berarti bahwa hanya ada satu salinan anggota data statis yang dibagikan oleh semua objek kelas, sementara setiap objek memiliki salinan sendiri anggota data non-statis. Jadi anggota data statis pada dasarnya adalah variabel global, yaitu anggota kelas.

Fungsi anggota non-statis dapat mengakses semua data anggota kelas: statis dan non-statis. Fungsi anggota statis hanya dapat beroperasi pada anggota data statis.

Salah satu cara untuk memikirkan hal ini adalah bahwa dalam C ++ anggota data statis dan fungsi anggota statis bukan milik objek apa pun, tetapi untuk seluruh kelas.

Dima
sumber
42
C ++ juga memiliki file-statis. Tidak perlu memasukkan C ke dalam ini.
Lightness Races dalam Orbit
17
Dalam C ++, fungsi statis adalah fungsi statis. Fungsi anggota statis adalah fungsi anggota statis, juga dikenal sebagai metode. Fakta bahwa C tidak memiliki anggota tidak berarti bahwa fungsinya "C".
Gerasimos R
3
apakah ada perbedaan antara global var dan kelas static var (kecuali namespace)?
Alexander Malakhov
3
Namespace adalah perbedaan utama. Perbedaan lainnya adalah bahwa Anda dapat membuat anggota data statis pribadi dan karenanya hanya dapat diakses dari dalam fungsi anggota kelas. Dengan kata lain, Anda memiliki kontrol lebih besar atas anggota data statis dibandingkan dengan variabel global.
Dima
2
Bisakah seseorang menjelaskan mengapa memikirkan fungsi statis sebagai pribadi untuk file .c-nya tidak sepenuhnya benar? Apa yang tersisa untuk dikatakan?
YoTengoUnLCD
78

Ada dua kegunaan untuk kata kunci statis ketika datang ke fungsi di C ++.

Yang pertama adalah untuk menandai fungsi sebagai memiliki hubungan internal sehingga tidak dapat direferensikan dalam unit terjemahan lainnya. Penggunaan ini tidak digunakan lagi dalam C ++. Ruang nama yang tidak disebutkan namanya lebih disukai untuk penggunaan ini.

// inside some .cpp file:

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

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

Penggunaan kedua adalah dalam konteks kelas. Jika kelas memiliki fungsi anggota statis, itu berarti fungsi tersebut adalah anggota kelas (dan memiliki akses biasa ke anggota lain), tetapi tidak perlu dipanggil melalui objek tertentu. Dengan kata lain, di dalam fungsi itu, tidak ada pointer "ini".

Brian Neal
sumber
1
Pertanyaannya adalah tentang statis di c.
Deqing
8
@Deqing pertanyaan awalnya ditandai C ++ dan penulis berbicara tentang menggunakan file ".cpp".
Brian Neal
57

Contoh lingkup multi-file runnable minimal

Di sini saya menggambarkan bagaimana staticmemengaruhi 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 staticjika Anda bisa.

Dalam pemrograman C, file sering digunakan untuk mewakili "kelas", dan staticfungsi mewakili metode "pribadi" kelas.

Pola C yang umum adalah meneruskan thisstruct 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 staticadalah " penentu kelas penyimpanan".

6.2.2 / 3 "Tautan pengidentifikasi" kata staticmenyiratkan 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 linkageberperilaku 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_LOCALmengikat.

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. Valuehanya offset mereka ke dalam .bssbagian, jadi kami perkirakan berbeda.

STB_LOCALdidokumentasikan 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, sfsimbol 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

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
3
Catatan: void f() { puts("sf"); }(yaitu dua definisi f()) menyebabkan perilaku yang tidak terdefinisi tanpa diagnostik yang diperlukan. Ini masalah kualitas tautan untuk benar-benar melihat pesan kesalahan.
MM
2
Ini adalah penjelasan terbaik dan tepat! Daripada Anda!
Aqua
20

Berikut ini adalah tentang fungsi C biasa - dalam kelas C ++ pengubah 'statis' memiliki arti lain.

Jika Anda hanya memiliki satu file, pengubah ini sama sekali tidak membuat perbedaan. Perbedaannya muncul pada proyek yang lebih besar dengan banyak file:

Dalam C, setiap "modul" (kombinasi dari sample.c dan sample.h) dikompilasi secara independen dan setelah itu setiap file objek yang dikompilasi (sample.o) dihubungkan bersama-sama ke file yang dapat dieksekusi oleh linker.

Katakanlah Anda memiliki beberapa file yang Anda sertakan dalam file utama Anda dan dua di antaranya memiliki fungsi yang hanya digunakan secara internal untuk kenyamanan yang disebut add(int a, b)- kompiler akan dengan mudah membuat file objek untuk dua modul tersebut, tetapi linker akan melakukan kesalahan, karena ia menemukan dua fungsi dengan nama yang sama dan tidak tahu mana yang harus digunakan (bahkan jika tidak ada yang ditautkan, karena mereka tidak digunakan di tempat lain tetapi dalam file itu sendiri).

Inilah sebabnya mengapa Anda membuat fungsi ini, yang hanya digunakan internal, fungsi statis. Dalam hal ini kompiler tidak membuat tipikal "Anda dapat menautkan hal ini" -flag untuk tautan, sehingga tautan tersebut tidak melihat fungsi ini dan tidak akan menghasilkan kesalahan.

dersimn
sumber
16

Pertama: Ini umumnya ide yang buruk untuk memasukkan .cppfile ke file lain - itu mengarah ke masalah seperti ini :-) Cara normal adalah membuat unit kompilasi terpisah, dan menambahkan file header untuk file yang disertakan.

Kedua:

C ++ memiliki beberapa terminologi yang membingungkan di sini - saya tidak tahu tentangnya sampai ditunjukkan dalam komentar.

a) static functions- diwarisi dari C, dan apa yang Anda bicarakan di sini. Di luar kelas apa pun. Fungsi statis berarti tidak terlihat di luar unit kompilasi saat ini - jadi dalam kasus Anda a.obj memiliki salinan dan kode Anda yang lain memiliki salinan independen. (Kembung executable terakhir dengan banyak salinan kode).

b) static member function- istilah Orientasi Objek apa yang disebut metode statis . Tinggal di dalam kelas. Anda menyebutnya dengan kelas daripada melalui instance objek.

Dua definisi fungsi statis yang berbeda ini sama sekali berbeda. Hati-hati - ini naga.

Douglas Leeder
sumber
Yah, saya melakukannya hanya untuk membersihkan ruang TEMPORARILY di main.cpp sampai saya memutuskan bagaimana mengatur file ke dalam perpustakaan bersama dengan .hpp yang tepat. Apakah ada ide yang lebih baik bagaimana melakukan ini?
Slava V
1
Terminologi yang benar dalam C ++ adalah fungsi anggota, bukan metode. Tidak ada "metode" di C ++ legalese. Metode adalah istilah OO umum. C ++ mengimplementasikannya melalui fungsi anggota.
Brian Neal
14

definisi fungsi statis akan menandai simbol ini sebagai internal. Jadi itu tidak akan terlihat untuk menghubungkan dari luar, tetapi hanya untuk fungsi-fungsi dalam unit kompilasi yang sama, biasanya file yang sama

raimue
sumber
7

Fungsi statis adalah fungsi yang dapat dipanggil pada kelas itu sendiri, sebagai lawan dari instance kelas.

Misalnya non-statis adalah:

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

Metode ini berfungsi pada instance kelas, bukan kelas itu sendiri. Namun Anda dapat memiliki metode statis yang dapat bekerja tanpa harus memiliki instance. Ini kadang-kadang digunakan dalam pola Pabrik:

Person* tom = Person::createNewPerson();
Bayan
sumber
2
Sepertinya saya bahwa Anda berbicara tentang "metode" statis, bukan "fungsi" ??
Slava V
Saya berasumsi Anda merujuk ke fungsi statis di dalam kelas.
Burung beo
Jika saya tahu "metode" disebut "metode fungsi" di C ++, saya akan lebih jelas tentang itu. Nah, sekarang saya lakukan :) Terima kasih pula
Slava V
5
Tidak ada "metode" di C ++, hanya fungsi. Standar C ++ tidak pernah menyebutkan "metode", hanya "fungsi".
Brian Neal
1
@Puddle Saya tahu apa yang Anda katakan tetapi dalam standar C ++ tidak ada definisi "metode". C ++ hanya memiliki fungsi, dari berbagai macam. "Metode" adalah istilah OO umum dan digunakan dalam bahasa lain dan secara informal dalam bahasa C ++. Suatu metode secara resmi dikenal sebagai "fungsi anggota" dalam C ++.
Brian Neal
7

Minor nit: fungsi statis dapat dilihat oleh unit terjemahan, yang untuk sebagian besar kasus praktis adalah fungsi file tersebut. Kesalahan yang Anda peroleh biasanya disebut sebagai pelanggaran Aturan Satu Definisi.

Standar mungkin mengatakan sesuatu seperti:

"Setiap program harus mengandung tepat satu definisi dari setiap fungsi atau objek noninline yang digunakan dalam program itu; tidak diperlukan diagnostik."

Itu adalah cara C memandang fungsi statis. Namun ini sudah usang dalam C ++.

Di C ++, selain itu, Anda dapat mendeklarasikan fungsi anggota statis. Ini sebagian besar adalah metafungsi yaitu mereka tidak menggambarkan / memodifikasi perilaku objek / negara tetapi bertindak pada seluruh kelas itu sendiri. Juga, ini berarti bahwa Anda tidak perlu membuat objek untuk memanggil fungsi anggota statis. Selanjutnya, ini juga berarti, Anda hanya mendapatkan akses ke variabel anggota statis dari dalam fungsi tersebut.

Saya akan menambahkan contoh Parrot pola Singleton yang didasarkan pada semacam ini fungsi anggota statis untuk mendapatkan / menggunakan objek tunggal sepanjang masa program.

secara langsung
sumber
7

Jawaban untuk fungsi statis tergantung pada bahasa:

1) Dalam bahasa tanpa OOPS seperti C, itu berarti bahwa fungsi tersebut hanya dapat diakses dalam file yang ditentukan.

2) Dalam bahasa dengan OOPS seperti C ++, itu berarti bahwa fungsi tersebut dapat dipanggil langsung di kelas tanpa membuat turunannya.

pengguna2410022
sumber
Ini tidak benar. Penjelasan paragraf kedua Anda mengacu pada " fungsi anggota statis " dari suatu kelas, bukan " fungsi statis ". Dalam C ++, fungsi yang memenuhi syarat staticmemiliki cakupan file juga, seperti di C.
RobertS mendukung Monica Cellio
0

Karena fungsi statis hanya terlihat di file ini. Sebenarnya, kompiler dapat melakukan beberapa optimasi untuk Anda jika Anda menyatakan "statis" untuk beberapa fungsi.

Ini adalah contoh sederhana.

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);
} 

Dan kompilasi dengan

gcc -o main main.c

Anda akan melihatnya gagal. Karena Anda bahkan tidak mengimplementasikan fungsi ghost ().

Tetapi bagaimana jika kita menggunakan perintah berikut.

gcc -DTEST -O2 -o main main.c

Ini berhasil , dan program ini dapat dijalankan secara normal.

Mengapa? Ada 3 poin kunci.

  1. -O2: Level optimisasi kompiler setidaknya 2.
  2. -DTEST: Tentukan TEST, jadi test () tidak akan dipanggil.
  3. Didefinisikan "statis" untuk menguji ().

Hanya jika 3 kondisi ini semuanya benar, Anda dapat melewati kompilasi. Karena deklarasi "statis" ini, kompiler dapat mengonfirmasi bahwa test () tidak akan pernah dipanggil di file lain. Kompiler Anda dapat menghapus test () saat kompilasi. Karena kita tidak memerlukan test (), tidak masalah apakah ghost () didefinisikan atau diimplementasikan.

PoJyun Chiou
sumber
0

" Apa staticfungsi " "di C? "

Mari kita mulai dari awal.

Semuanya didasarkan pada hal yang disebut "tautan":

" Suatu pengidentifikasi yang dideklarasikan dalam lingkup yang berbeda atau dalam cakupan yang sama lebih dari satu kali dapat dibuat untuk merujuk ke objek atau fungsi yang sama dengan proses yang disebut tautan. 29) Ada tiga jenis hubungan: eksternal, internal, dan tidak ada. "

Sumber: C18, 6.2.2 / 1


"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 Setiap pernyataan pengidentifikasi tanpa hubungan menunjukkan suatu entitas yang unik. "

Sumber: C18, 6.2.2 / 2


Jika suatu fungsi didefinisikan tanpa specifier kelas penyimpanan, fungsi tersebut memiliki externtautan secara default:

"Jika deklarasi pengidentifikasi untuk suatu fungsi tidak memiliki specifier kelas penyimpanan, keterkaitannya ditentukan persis seolah-olah dinyatakan dengan extern specifier kelas penyimpanan ."

Sumber: C18, 6.2.2 / 5

Itu berarti bahwa - jika program Anda berisi beberapa unit terjemahan / file sumber ( .catau .cpp) - fungsinya terlihat di semua unit terjemahan / file sumber yang dimiliki program Anda.

Ini bisa menjadi masalah dalam beberapa kasus. Bagaimana jika Anda ingin menggunakan dua fungsi yang berbeda (definisi), tetapi dengan nama fungsi yang sama dalam dua konteks yang berbeda (sebenarnya konteks file).

Dalam C dan C ++, statickualifikasi kelas penyimpanan yang diterapkan ke fungsi pada lingkup file (bukan fungsi anggota statis kelas di C ++ atau fungsi dalam blok lain) sekarang hadir untuk membantu dan menandakan bahwa fungsi masing-masing hanya terlihat di dalam unit terjemahan / file sumber yang didefinisikan dan bukan dalam TLU / file lainnya.

"Jika deklarasi pengidentifikasi lingkup file untuk objek atau fungsi berisi statis specifier kelas penyimpanan , pengidentifikasi memiliki tautan internal. 30)"


30) Suatu deklarasi fungsi dapat berisi kelas penyimpanan yang spesifik hanya jika berada pada ruang lingkup file; lihat 6.7.1.

Sumber: C18, 6.2.2 / 3


Dengan demikian, staticfungsi A hanya masuk akal, jika:

  1. Program Anda berisi beberapa unit terjemahan / file sumber ( .catau.cpp ).

    dan

  2. Anda ingin membatasi ruang lingkup fungsi ke file, di mana fungsi spesifik didefinisikan.

Jika tidak kedua persyaratan ini cocok, Anda tidak perlu membahas tentang kualifikasi fungsi sebagai static.


Catatan Samping:

  • Seperti yang telah disebutkan, staticfungsi A sama sekali tidak ada perbedaan sama sekali antara C dan C ++, karena ini adalah fitur C ++ yang diwarisi dari C.

    Tidak masalah bahwa di komunitas C ++, ada debat yang memilukan tentang depresiasi fungsi kualifikasi karena staticdibandingkan dengan penggunaan ruang nama yang tidak disebutkan namanya , mula-mula diinisialisasi dengan paragraf yang salah tempat dalam standar C ++ 03, menyatakan penggunaan fungsi statis seperti yang sudah usang yang segera direvisi oleh komite itu sendiri dan dihapus di C ++ 11.

    Ini tunduk pada berbagai pertanyaan SO:

    Ruang nama tanpa nama / anonim vs. fungsi statis

    Keunggulan namespace tanpa nama dibandingkan statis?

    Mengapa namespace tanpa nama adalah alternatif "superior" dari statis?

    Penghentian kata kunci statis ... tidak lagi?

    Faktanya, ini belum usang per standar C ++. Dengan demikian, penggunaan staticfungsi masih sah. Bahkan jika ruang nama yang tidak disebutkan namanya memiliki keuntungan, diskusi tentang menggunakan atau tidak menggunakan fungsi statis di C ++ tunduk pada satu pikiran (berdasarkan pendapat) dan dengan yang tidak cocok untuk situs web ini.

RobertS mendukung Monica Cellio
sumber