static volatile unsigned char PORTB @ 0x06;
Ini adalah baris kode dalam file header mikrokontroler PIC. The @
operator yang digunakan untuk menyimpan nilai PORTB dalam alamat 0x06
, yang merupakan mendaftar di dalam PIC controller yang mewakili PORTB. Sampai saat ini, saya punya ide yang jelas.
Baris ini dinyatakan sebagai variabel global di dalam file header ( .h
). Jadi, dari apa yang saya ketahui tentang bahasa C, "variabel global statis" tidak dapat dilihat oleh file lain - atau, sederhananya, variabel / fungsi global statis tidak dapat digunakan di luar file saat ini.
Lalu, bagaimana kata kunci ini PORTB
dapat dilihat oleh file sumber utama saya dan banyak file header lainnya yang saya buat secara manual?
Pada file sumber utama saya, saya hanya menambahkan file header #include pic.h
Apakah ini ada hubungannya dengan pertanyaan saya?
static
global terlihat di dalam seluruh unit kompilasi tunggal, dan tidak diekspor lebih dari itu. Mereka sepertiprivate
anggota kelas di OOP. Yaitu setiap variabel yang perlu dibagi antara fungsi yang berbeda di dalam unit kompilasi tetapi tidak seharusnya terlihat di luar yang seharusnya benar-benar terjadistatic
. Ini juga mengurangi "clobbering" dari namespace global program.Jawaban:
Kata kunci 'statis' dalam C memiliki dua arti yang berbeda secara fundamental.
Lingkup Pembatas
Dalam konteks ini, 'statis' berpasangan dengan 'extern' untuk mengontrol ruang lingkup variabel atau nama fungsi. Statis menyebabkan variabel atau nama fungsi hanya tersedia dalam satu unit kompilasi dan hanya tersedia untuk kode yang ada setelah deklarasi / definisi dalam teks unit kompilasi.
Batasan ini sendiri sebenarnya hanya berarti sesuatu jika dan hanya jika Anda memiliki lebih dari satu unit kompilasi dalam proyek Anda. Jika Anda hanya memiliki satu unit kompilasi, maka ia masih melakukan hal-hal tetapi efeknya sebagian besar tidak ada gunanya (kecuali jika Anda suka menggali file objek untuk membaca apa yang dihasilkan kompiler.)
Seperti disebutkan, kata kunci ini dalam konteks ini berpasangan dengan kata kunci 'extern', yang melakukan sebaliknya - dengan membuat variabel atau nama fungsi ditautkan dengan nama yang sama yang ditemukan di unit kompilasi lainnya. Jadi, Anda dapat melihat 'statis' yang membutuhkan variabel atau nama untuk ditemukan dalam unit kompilasi saat ini, sementara 'eksternal' memungkinkan hubungan unit kompilasi silang.
Seumur Hidup Statis
Seumur hidup statis berarti bahwa variabel ada sepanjang durasi program (betapapun lama itu.) Ketika Anda menggunakan 'statis' untuk mendeklarasikan / mendefinisikan variabel dalam suatu fungsi, itu berarti bahwa variabel dibuat beberapa saat sebelum penggunaan pertama ( yang berarti, setiap kali saya mengalaminya, bahwa variabel dibuat sebelum main () dimulai) dan tidak dihancurkan setelahnya. Bahkan ketika eksekusi fungsi selesai dan kembali ke pemanggilnya. Dan seperti halnya variabel seumur hidup statis yang dinyatakan di luar fungsi, mereka diinisialisasi pada saat yang sama - sebelum main () mulai - ke nol semantik (jika tidak ada inisialisasi disediakan) atau ke nilai eksplisit yang ditentukan, jika diberikan.
Ini berbeda dari variabel fungsi tipe 'otomatis', yang dibuat baru (atau, seolah-olah baru) setiap kali fungsi dimasukkan dan kemudian dihancurkan (atau, seolah-olah dihancurkan) ketika fungsi keluar.
Berbeda dengan dampak penerapan 'statis' pada definisi variabel di luar fungsi, yang berdampak langsung pada ruang lingkupnya, menyatakan variabel fungsi (dalam suatu fungsi tubuh, jelas) sebagai 'statis' tidak memiliki dampak pada cakupannya. Lingkup ditentukan oleh fakta bahwa itu didefinisikan dalam suatu fungsi tubuh. Variabel seumur hidup statis yang didefinisikan dalam fungsi memiliki cakupan yang sama dengan variabel 'otomatis' lainnya yang didefinisikan dalam badan fungsi - ruang lingkup fungsi.
Ringkasan
Jadi kata kunci 'statis' memiliki konteks yang berbeda dengan jumlah yang berarti "makna yang sangat berbeda." Alasan itu digunakan dalam dua cara, seperti ini, adalah untuk menghindari penggunaan kata kunci lain. (Ada diskusi panjang tentang hal itu.) Ia merasa bahwa programmer dapat mentolerir penggunaan dan nilai menghindari kata kunci lain dalam bahasa lebih penting (daripada argumen sebaliknya.)
(Semua variabel yang dinyatakan di luar fungsi memiliki masa pakai statis dan tidak perlu kata kunci 'statis' untuk menjadikannya benar. Jadi, jenis kata kunci yang dibebaskan ini yang akan digunakan di sana berarti sesuatu yang sama sekali berbeda: 'hanya dapat dilihat dalam satu kompilasi) unit. "Ini semacam retas, semacamnya.)
Catatan Khusus
Kata 'statis' di sini harus diartikan berarti bahwa penghubung tidak akan berusaha mencocokkan beberapa kejadian PORTB yang dapat ditemukan di lebih dari satu unit kompilasi (dengan asumsi kode Anda memiliki lebih dari satu.)
Ini menggunakan sintaks khusus (non-portabel) untuk menentukan "lokasi" (atau nilai numerik label yang biasanya merupakan alamat) dari PORTB. Jadi linker diberikan alamatnya dan tidak perlu menemukannya untuk itu. Jika Anda memiliki dua unit kompilasi yang menggunakan baris ini, mereka akhirnya akan menunjuk ke tempat yang sama. Jadi tidak perlu memberi label 'eksternal', di sini.
Seandainya mereka menggunakan 'eksternal' mungkin menimbulkan masalah. Linker kemudian dapat melihat (dan akan mencoba untuk mencocokkan) beberapa referensi ke PORTB yang ditemukan dalam beberapa kompilasi. Jika semuanya menentukan alamat seperti ini, dan alamatnya TIDAK sama untuk beberapa alasan [kesalahan?], Lalu apa yang harus dilakukan? Mengeluh? Atau? (Secara teknis, dengan 'extern' aturan praktisnya adalah bahwa hanya SATU unit kompilasi yang akan menentukan nilainya dan yang lain tidak.)
Hanya saja lebih mudah untuk menandainya sebagai 'statis', menghindari membuat linker khawatir tentang konflik, dan hanya menyalahkan kesalahan apa pun untuk alamat yang tidak cocok pada siapa pun yang mengubah alamat menjadi sesuatu yang tidak seharusnya.
Bagaimanapun, variabel diperlakukan sebagai 'masa hidup statis'. (Dan 'mudah berubah'.)
Sebuah deklarasi bukanlah definisi , tetapi semua definisi deklarasi
Dalam C, definisi membuat objek. Itu juga mendeklarasikannya. Tetapi deklarasi biasanya tidak (lihat catatan di bawah) membuat objek.
Berikut ini adalah definisi dan deklarasi:
Berikut ini bukan definisi, tetapi hanya deklarasi:
Perhatikan bahwa deklarasi tidak membuat objek aktual. Mereka hanya menyatakan rincian tentang hal itu, yang kemudian dapat digunakan oleh kompiler untuk membantu menghasilkan kode yang benar dan untuk memberikan pesan peringatan dan kesalahan, yang sesuai.
Di atas, saya katakan "biasanya," dengan penuh pertimbangan. Dalam beberapa kasus, deklarasi dapat membuat objek dan karena itu dipromosikan ke definisi oleh linker (tidak pernah oleh kompiler.) Jadi, bahkan dalam kasus yang jarang terjadi ini, kompiler C masih berpikir bahwa deklarasi hanyalah deklarasi. Ini adalah fase tautan yang membuat promosi yang diperlukan dari beberapa pernyataan. Ingatlah ini dengan cermat.
Dalam contoh di atas, jika ternyata hanya ada deklarasi untuk "extern int b;" di semua unit kompilasi tertaut, maka tautan tersebut dibebankan tanggung jawab untuk membuat definisi. Ketahuilah bahwa ini adalah acara tautan waktu. Compiler sama sekali tidak sadar, selama kompilasi. Ini hanya dapat ditentukan pada waktu tautan, jika suatu deklarasi jenis ini paling dipromosikan.
Kompiler sadar bahwa "static int a;" tidak dapat dipromosikan oleh tautan pada waktu tautan, jadi ini sebenarnya adalah definisi pada waktu kompilasi .
sumber
extern
, dan itu akan menjadi cara C yang lebih tepat untuk melakukannya: Mendeklarasikan variabelextern
dalam file header untuk dimasukkan beberapa kali dalam program dan mendefinisikannya dalam beberapa file non-header untuk dikompilasi dan terhubung tepat sekali. Setelah semua,PORTB
yang seharusnya menjadi persis salah satu contoh dari variabel yang berbeda cu dapat merujuk. Jadi penggunaan distatic
sini adalah jenis jalan pintas yang mereka ambil untuk menghindari kebutuhan file .c selain file header.static
s tidak terlihat di luar unit kompilasi saat ini , atau "unit terjemahan". Ini tidak sama dengan file yang sama .Perhatikan bahwa Anda memasukkan file header ke file sumber apa pun di mana Anda mungkin memerlukan variabel yang dideklarasikan dalam header. Inklusi ini menjadikan file header bagian dari unit terjemahan saat ini dan (contoh dari) variabel terlihat di dalamnya.
sumber
Files included by using the #include preprocessor directive become part of the compilation unit.
Saat Anda memasukkan file header (.h) dalam file .c, anggap itu memasukkan konten header di file sumber, dan sekarang, ini adalah unit kompilasi Anda. Jika Anda mendeklarasikan variabel statis atau fungsi dalam file .c, Anda dapat menggunakannya hanya dalam file yang sama, yang pada akhirnya, akan menjadi unit kompilasi lain.Saya akan mencoba merangkum komentar dan jawaban @ JimmyB dengan contoh penjelasan:
Misalkan kumpulan file ini:
static_test.c:
static.h:
no_static.h:
static_src.c:
Anda dapat mengkompilasi dan menjalankan kode menggunakan
gcc -o static_test static_src.c static_test.c -DUSE_STATIC=1; ./static_test
untuk menggunakan header statis ataugcc -o static_test static_src.c static_test.c -DUSE_STATIC=0; ./static_test
untuk menggunakan header non-statis.Perhatikan bahwa dua unit kompilasi hadir di sini: static_src dan static_test. Ketika Anda menggunakan versi statis header (
-DUSE_STATIC=1
), versivar
dansay_hello
akan tersedia untuk setiap unit kompilasi, ini, kedua unit dapat menggunakannya, tetapi periksa bahwa meskipunvar_add_one()
bertahap fungsi yangvar
variabel, ketika fungsi utama mencetak nyavar
variabel , masih 64:Sekarang, jika Anda mencoba untuk mengkompilasi dan menjalankan kode, menggunakan versi non-statis (
-DUSE_STATIC=0
), itu akan menimbulkan kesalahan penautan karena definisi variabel digandakan:Semoga ini bisa membantu Anda mengklarifikasi masalah ini.
sumber
#include pic.h
kira-kira berarti "menyalin isi pic.h ke file saat ini". Akibatnya, setiap file yang termasukpic.h
mendapatkan definisi lokalnya sendiriPORTB
.Mungkin Anda bertanya-tanya mengapa tidak ada definisi global tunggal
PORTB
. Alasannya cukup sederhana: Anda hanya dapat mendefinisikan sebuah variabel global dalam satu file C, jadi jika Anda ingin menggunakanPORTB
dalam beberapa file dalam proyek Anda, Anda akan perlupic.h
dengan deklarasi dariPORTB
danpic.c
dengan nya definisi . Membiarkan setiap file C menentukan salinannya sendiriPORTB
membuat pembuatan kode lebih mudah, karena Anda tidak harus memasukkan file proyek yang tidak Anda tulis.Manfaat tambahan variabel statis vs global adalah Anda mendapatkan lebih sedikit konflik penamaan. File AC yang tidak menggunakan fitur perangkat keras MCU (dan dengan demikian tidak termasuk
pic.h
) dapat menggunakan namaPORTB
untuk tujuannya sendiri. Bukannya itu ide yang baik untuk melakukannya dengan sengaja, tetapi ketika Anda mengembangkan misalnya perpustakaan matematika agnostik MCU, Anda akan terkejut betapa mudahnya untuk secara tidak sengaja menggunakan kembali nama yang digunakan oleh salah satu MCU di luar sana.sumber
Sudah ada beberapa jawaban yang baik, tetapi saya pikir penyebab kebingungan perlu ditangani secara sederhana dan langsung:
Deklarasi PORTB bukan standar C. Ini adalah perpanjangan dari bahasa pemrograman C yang hanya bekerja dengan kompiler PIC. Perlu ekstensi karena PICs tidak dirancang untuk mendukung C.
Penggunaan
static
kata kunci di sini membingungkan karena Anda tidak akan pernah menggunakanstatic
cara itu dalam kode normal. Untuk variabel global, Anda akan menggunakanextern
di header, bukanstatic
. Tetapi PORTB bukan variabel normal . Ini adalah retasan yang memberi tahu kompiler untuk menggunakan instruksi perakitan khusus untuk mendaftar IO. Mendeklarasikan PORTBstatic
membantu mengelabui kompiler untuk melakukan hal yang benar.Ketika digunakan pada lingkup file,
static
membatasi ruang lingkup variabel atau fungsi untuk file itu. "File" berarti file C dan apa pun yang disalin ke dalamnya oleh preprosesor. Saat Anda menggunakan #include, Anda menyalin kode ke file C. Karena itulah menggunakanstatic
header tidak masuk akal - alih-alih satu variabel global, setiap file yang # termasuk header akan mendapatkan salinan variabel yang terpisah.Bertentangan dengan kepercayaan populer,
static
selalu berarti hal yang sama: alokasi statis dengan ruang lingkup terbatas. Inilah yang terjadi pada variabel sebelum dan sesudah diumumkanstatic
:Yang membuatnya membingungkan adalah bahwa perilaku default variabel tergantung pada di mana mereka didefinisikan.
sumber
Alasan file utama dapat melihat definisi port "statis" adalah karena arahan #include. Arahan itu setara dengan memasukkan seluruh file header ke kode sumber Anda pada baris yang sama dengan arahan itu sendiri.
Kompiler microchip XC8 memperlakukan file .c dan .h persis sama sehingga Anda dapat menempatkan definisi variabel Anda di salah satu.
Biasanya file header berisi referensi "extern" ke variabel yang didefinisikan di tempat lain (biasanya file .c).
Variabel port perlu ditentukan pada alamat memori tertentu yang cocok dengan perangkat keras yang sebenarnya. Jadi definisi aktual (non eksternal) perlu ada di suatu tempat.
Saya hanya bisa menebak mengapa Microchip corporation memilih untuk memasukkan definisi aktual dalam file .h. Tebakan yang mungkin adalah bahwa mereka hanya menginginkan satu file (.h) alih-alih 2 (.h dan .c) (untuk mempermudah pengguna).
Tetapi jika Anda memasukkan definisi variabel aktual dalam file header dan kemudian memasukkan header itu ke beberapa file sumber maka linker akan mengeluh bahwa variabel didefinisikan beberapa kali.
Solusinya adalah mendeklarasikan variabel sebagai statis maka setiap definisi diperlakukan sebagai lokal ke file objek itu dan linker tidak akan mengeluh.
sumber