Saya telah membaca beberapa artikel dan jawaban Stack Exchange tentang penggunaan volatile
kata kunci untuk mencegah kompiler menerapkan optimasi pada objek yang dapat berubah dengan cara yang tidak dapat ditentukan oleh kompiler.
Jika saya membaca dari ADC (sebut saja variabel adcValue
), dan saya menyatakan variabel ini sebagai global, haruskah saya menggunakan kata kunci volatile
dalam kasus ini?
Tanpa menggunakan
volatile
kata kunci// Includes #include "adcDriver.h" // Global variables uint16_t adcValue; // Some code void readFromADC(void) { adcValue = readADC(); }
Menggunakan
volatile
kata kunci// Includes #include "adcDriver.h" // Global variables volatile uint16_t adcValue; // Some code void readFromADC(void) { adcValue = readADC(); }
Saya mengajukan pertanyaan ini karena ketika debugging, saya dapat melihat tidak ada perbedaan antara kedua pendekatan meskipun praktik terbaik mengatakan bahwa dalam kasus saya (variabel global yang berubah langsung dari perangkat keras), maka menggunakan volatile
adalah wajib.
microcontroller
c
embedded
Pryda
sumber
sumber
if(x==1) x=1;
penulisan dapat dioptimalkan untuk non volatilex
dan tidak dapat dioptimalkan jikax
volatile. OTOH jika diperlukan instruksi khusus untuk mengakses perangkat eksternal, terserah Anda untuk menambahkannya (mis. Jika rentang memori perlu dibuat tuliskan melalui).Jawaban:
Definisi dari
volatile
volatile
memberitahu kompiler bahwa nilai variabel dapat berubah tanpa diketahui kompiler. Oleh karena itu kompiler tidak dapat menganggap nilai tidak berubah hanya karena program C tampaknya tidak mengubahnya.Di sisi lain, itu berarti bahwa nilai variabel mungkin diperlukan (baca) di tempat lain yang tidak diketahui oleh kompiler, oleh karena itu ia harus memastikan bahwa setiap tugas pada variabel benar-benar dilakukan sebagai operasi tulis.
Gunakan kasing
volatile
diperlukan saatEfek dari
volatile
Ketika sebuah variabel dideklarasikan
volatile
, kompiler harus memastikan bahwa setiap penugasan dalam kode program tercermin dalam operasi penulisan yang sebenarnya, dan bahwa setiap pembacaan dalam kode program membaca nilai dari memori (yang dipetakan).Untuk variabel yang tidak mudah menguap, kompiler menganggap ia tahu jika / ketika nilai variabel berubah dan dapat mengoptimalkan kode dengan cara yang berbeda.
Untuk satu, kompiler dapat mengurangi jumlah baca / tulis ke memori, dengan menjaga nilai dalam register CPU.
Contoh:
Di sini, kompiler mungkin bahkan tidak akan mengalokasikan RAM untuk
result
variabel, dan tidak akan pernah menyimpan nilai-nilai perantara di mana pun kecuali dalam register CPU.Jika
result
volatile, setiap kejadianresult
dalam kode C akan memerlukan kompiler untuk melakukan akses ke RAM (atau port I / O), yang mengarah ke kinerja yang lebih rendah.Kedua, kompiler dapat memesan ulang operasi pada variabel non-volatile untuk kinerja dan / atau ukuran kode. Contoh sederhana:
bisa dipesan ulang
yang dapat menyimpan instruksi assembler karena nilai
99
tidak harus dimuat dua kali.Jika
a
,b
danc
volatile kompiler harus memancarkan instruksi yang menetapkan nilai-nilai dalam urutan yang tepat seperti yang diberikan dalam program.Contoh klasik lainnya adalah seperti ini:
Jika, dalam kasus ini,
signal
tidakvolatile
, kompiler akan 'berpikir' bahwawhile( signal == 0 )
mungkin merupakan infinite loop (karenasignal
tidak akan pernah diubah oleh kode di dalam loop ) dan mungkin menghasilkan setara denganPertimbangan penanganan
volatile
nilaiSeperti yang dinyatakan di atas,
volatile
variabel dapat memperkenalkan penalti kinerja ketika diakses lebih sering daripada yang sebenarnya diperlukan. Untuk mengurangi masalah ini, Anda bisa "membatalkan volatile" nilainya dengan menetapkan variabel yang tidak mudah menguap, sepertiIni mungkin sangat bermanfaat di ISR di mana Anda ingin secepat mungkin tidak mengakses perangkat keras atau memori yang sama beberapa kali ketika Anda tahu itu tidak diperlukan karena nilainya tidak akan berubah saat ISR Anda berjalan. Ini umum ketika ISR adalah 'penghasil' nilai untuk variabel, seperti
sysTickCount
pada contoh di atas. Pada AVR akan sangat menyakitkan untuk memiliki fungsidoSysTick()
mengakses empat byte yang sama dalam memori (empat instruksi = 8 siklus CPU per akses kesysTickCount
) lima atau enam kali alih-alih hanya dua kali, karena programmer tahu bahwa nilainya tidak akan diubah dari beberapa kode lain saatdoSysTick()
menjalankannya.Dengan trik ini, Anda pada dasarnya melakukan hal yang sama persis seperti yang dilakukan oleh kompiler untuk variabel non-volatil, yaitu membacanya dari memori hanya ketika harus, menyimpan nilai dalam register untuk beberapa waktu dan menulis kembali ke memori hanya ketika harus ; tetapi kali ini, Anda tahu lebih baik daripada kompiler jika / ketika membaca / menulis harus terjadi, jadi Anda membebaskan kompiler dari tugas optimasi ini dan melakukannya sendiri.
Keterbatasan
volatile
Akses non-atom
volatile
tidak tidak memberikan akses atom untuk variabel multi-kata. Untuk kasus-kasus tersebut, Anda harus memberikan pengecualian bersama dengan cara lain, selain menggunakanvolatile
. Pada AVR, Anda dapat menggunakanATOMIC_BLOCK
dari<util/atomic.h>
ataucli(); ... sei();
panggilan sederhana . Makro masing-masing bertindak sebagai penghalang memori juga, yang penting ketika datang ke urutan akses:Perintah eksekusi
volatile
membebankan pesanan eksekusi ketat hanya sehubungan dengan variabel volatil lainnya. Ini artinya, misalnyadijamin pertama menetapkan 1 untuk
i
dan kemudian menetapkan 2 untukj
. Namun, itu tidak dijamin yanga
akan ditugaskan di antara; kompiler dapat melakukan tugas itu sebelum atau setelah potongan kode, pada dasarnya setiap saat hingga pembacaan pertama (terlihat)a
.Jika bukan karena penghalang memori makro yang disebutkan di atas, kompiler akan diizinkan untuk menerjemahkan
untuk
atau
(Demi kelengkapan, saya harus mengatakan bahwa hambatan ingatan, seperti yang tersirat oleh makro sei / cli, dapat benar-benar meniadakan penggunaan
volatile
, jika semua akses dihubungkan dengan hambatan-hambatan ini.)sumber
An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects.
Lebih banyak orang harus membacanya.cli
/sei
ini solusi yang terlalu berat jika satu-satunya tujuan Anda adalah mencapai penghalang memori, bukan mencegah gangguan. Makro ini menghasilkan aktualcli
/sei
instruksi, dan juga memori clobber, dan clobbering inilah yang menghasilkan penghalang. Untuk hanya memiliki penghalang memori tanpa menonaktifkan interupsi Anda dapat mendefinisikan makro Anda sendiri dengan tubuh seperti__asm__ __volatile__("":::"memory")
(yaitu kode perakitan kosong dengan memory clobber).volatile
ada titik sekuens, dan segala sesuatu setelahnya harus "diurutkan setelah". Berarti ungkapan itu adalah semacam penghalang memori. Vendor kompiler memilih untuk menyebarkan semua jenis mitos untuk menempatkan tanggung jawab hambatan memori pada programmer tetapi itu melanggar aturan "mesin abstrak".volatile data_t data = {0}; set_mmio(&data); while (!data.ready);
.Kata kunci yang mudah menguap memberi tahu kompiler bahwa akses ke variabel memiliki efek yang dapat diamati. Itu berarti setiap kali kode sumber Anda menggunakan variabel kompiler HARUS membuat akses ke variabel. Jadilah akses baca atau tulis.
Efeknya adalah setiap perubahan pada variabel di luar aliran kode normal juga akan diamati oleh kode. Misal jika interrupt handler mengubah nilainya. Atau jika variabel sebenarnya adalah register perangkat keras yang berubah sendiri.
Manfaat besar ini juga merupakan kerugiannya. Setiap akses tunggal ke variabel melewati variabel dan nilainya tidak pernah disimpan dalam register untuk akses yang lebih cepat untuk jumlah waktu berapa pun. Itu berarti variabel volatil akan lambat. Besarnya lebih lambat. Jadi hanya gunakan volatile yang sebenarnya diperlukan.
Dalam kasus Anda, sejauh yang Anda tunjukkan kode, variabel global hanya diubah ketika Anda memperbaruinya sendiri
adcValue = readADC();
. Kompiler tahu kapan ini terjadi dan tidak akan pernah memegang nilai adcValue dalam register di sesuatu yang mungkin memanggilreadFromADC()
fungsi. Atau fungsi apa pun yang tidak diketahuinya. Atau apapun yang akan memanipulasi pointer yang mungkin menunjukadcValue
dan semacamnya. Benar-benar tidak perlu bergejolak karena variabel tidak pernah berubah dengan cara yang tidak terduga.sumber
volatile
segala sesuatu hanya karena , tetapi Anda juga tidak boleh menghindarinya dalam kasus-kasus di mana Anda berpikir itu secara sah diperlukan karena kekhawatiran kinerja preemptive, juga.Penggunaan utama kata kunci yang tidak stabil pada aplikasi C yang disematkan adalah untuk menandai variabel global yang ditulis ke dalam interrupt handler. Ini tentu bukan opsional dalam hal ini.
Tanpa itu, kompiler tidak dapat membuktikan bahwa nilai pernah ditulis setelah inisialisasi, karena tidak dapat membuktikan interrupt handler pernah dipanggil. Oleh karena itu ia berpikir dapat mengoptimalkan variabel dari keberadaan.
sumber
Ada dua kasus di mana Anda harus menggunakan
volatile
sistem embedded.Saat membaca dari register perangkat keras.
Itu berarti, register yang dipetakan memori itu sendiri, bagian dari periferal perangkat keras di dalam MCU. Kemungkinan akan memiliki beberapa nama samar seperti "ADC0DR". Register ini harus didefinisikan dalam kode C, baik melalui beberapa peta register yang dikirimkan oleh vendor alat, atau oleh Anda sendiri. Untuk melakukannya sendiri, Anda harus melakukannya (dengan asumsi 16 bit mendaftar):
di mana 0x1234 adalah alamat di mana MCU telah memetakan register. Karena
volatile
sudah menjadi bagian dari makro di atas, setiap akses ke sana akan memenuhi syarat volatile. Jadi kode ini baik-baik saja:Saat berbagi variabel antara ISR dan kode terkait menggunakan hasil ISR.
Jika Anda memiliki sesuatu seperti ini:
Maka kompiler mungkin berpikir: "adc_data selalu 0 karena tidak diperbarui di mana pun. Dan fungsi ADC0_interrupt () tidak pernah dipanggil, sehingga variabel tidak dapat diubah". Kompiler biasanya tidak menyadari bahwa interupsi dipanggil oleh perangkat keras, bukan oleh perangkat lunak. Jadi kompiler pergi dan menghapus kode
if(adc_data > 0){ do_stuff(adc_data); }
karena dianggap tidak pernah benar, menyebabkan bug yang sangat aneh dan sulit di-debug.Dengan mendeklarasikan
adc_data
volatile
, kompiler tidak diperbolehkan untuk membuat asumsi seperti itu dan tidak diizinkan untuk mengoptimalkan akses ke variabel.Catatan penting:
ISR harus selalu dinyatakan di dalam driver perangkat keras. Dalam hal ini, ADC ISR harus berada di dalam driver ADC. Tidak lain adalah pengemudi harus berkomunikasi dengan ISR - yang lainnya adalah pemrograman spageti.
Saat menulis C, semua komunikasi antara ISR dan program latar belakang harus dilindungi terhadap kondisi ras. Selalu , setiap saat, tanpa pengecualian. Ukuran bus data MCU tidak masalah, karena bahkan jika Anda melakukan satu salinan 8 bit dalam C, bahasa tidak dapat menjamin keaslian operasi. Tidak, kecuali Anda menggunakan fitur C11
_Atomic
. Jika fitur ini tidak tersedia, Anda harus menggunakan beberapa cara semaphore atau menonaktifkan interupsi saat membaca dll. Assembler inline adalah pilihan lain.volatile
tidak menjamin atomicity.Apa yang bisa terjadi adalah ini:
Nilai -Load dari stack ke register
-Interrupt terjadi
-Gunakan nilai dari register
Dan kemudian tidak masalah jika bagian "nilai pakai" adalah instruksi tunggal. Sayangnya, sebagian besar dari semua programmer sistem embedded tidak menyadari hal ini, mungkin menjadikannya bug sistem embedded paling umum yang pernah ada. Selalu terputus-putus, sulit diprovokasi, sulit ditemukan.
Contoh driver ADC yang ditulis dengan benar akan terlihat seperti ini (dengan asumsi C11
_Atomic
tidak tersedia):adc.h
adc.c
Kode ini mengasumsikan bahwa interupsi tidak dapat diganggu dengan sendirinya. Pada sistem seperti itu, boolean sederhana dapat bertindak sebagai semaphore, dan itu tidak perlu atomik, karena tidak ada salahnya jika interupsi terjadi sebelum boolean diatur. Sisi bawah dari metode yang disederhanakan di atas adalah bahwa ia akan membuang pembacaan ADC ketika kondisi balapan terjadi, menggunakan nilai sebelumnya sebagai gantinya. Ini dapat dihindari juga, tetapi kemudian kode berubah lebih kompleks.
Di sini
volatile
melindungi terhadap bug pengoptimalan. Itu tidak ada hubungannya dengan data yang berasal dari register perangkat keras, hanya bahwa data dibagikan dengan ISR.static
melindungi terhadap pemrograman spageti dan polusi namespace, dengan membuat variabel lokal ke pengemudi. (Ini bagus di aplikasi single-core, single-thread, tetapi tidak di yang multi-threaded.)sumber
semaphore
seharusnyavolatile
! Bahkan, itu yang paling kasus penggunaan dasar Wich panggilan untukvolatile
: Signal sesuatu dari satu konteks eksekusi yang lain. - Dalam contoh Anda, kompiler hanya bisa menghilangkansemaphore = true;
karena 'melihat' bahwa nilainya tidak pernah dibaca sebelum ditimpa olehsemaphore = false;
.Dalam cuplikan kode yang disajikan dalam pertanyaan, belum ada alasan untuk menggunakan volatile. Tidak relevan bahwa nilai
adcValue
berasal dari ADC. DanadcValue
menjadi global harus membuat Anda curiga apakahadcValue
harus volatile tetapi itu bukan alasan dengan sendirinya.Menjadi global adalah petunjuk karena membuka kemungkinan yang
adcValue
dapat diakses dari lebih dari satu konteks program. Konteks program mencakup penangan interupsi dan tugas RTOS. Jika variabel global diubah oleh satu konteks maka konteks program lainnya tidak dapat menganggap mereka tahu nilai dari akses sebelumnya. Setiap konteks harus membaca kembali nilai variabel setiap kali mereka menggunakannya karena nilainya mungkin telah diubah dalam konteks program yang berbeda. Konteks program tidak sadar ketika interupsi atau pengalihan tugas terjadi sehingga harus mengasumsikan bahwa variabel global apa pun yang digunakan oleh beberapa konteks dapat berubah di antara akses variabel apa pun karena kemungkinan pengalihan konteks. Ini adalah tujuan dari deklarasi volatile. Ini memberitahu kompiler bahwa variabel ini dapat berubah di luar konteks Anda jadi bacalah setiap akses dan jangan menganggap Anda sudah tahu nilainya.Jika variabel dipetakan ke alamat perangkat keras, maka perubahan yang dilakukan oleh perangkat keras secara efektif adalah konteks lain di luar konteks program Anda. Jadi pemetaan memori juga merupakan petunjuk. Misalnya, jika
readADC()
fungsi Anda mengakses nilai yang dipetakan memori untuk mendapatkan nilai ADC maka variabel yang dipetakan memori mungkin harus berubah-ubah.Jadi kembali ke pertanyaan Anda, jika ada lebih banyak kode Anda dan
adcValue
dapat diakses oleh kode lain yang berjalan dalam konteks yang berbeda, maka ya,adcValue
harus volatile.sumber
Hanya karena nilainya berasal dari beberapa register ADC perangkat keras, tidak berarti bahwa nilai tersebut "langsung" diubah oleh perangkat keras.
Dalam contoh Anda, Anda cukup memanggil readADC (), yang mengembalikan beberapa nilai register ADC. Ini bagus untuk kompiler, mengetahui bahwa adcValue diberi nilai baru pada saat itu.
Akan berbeda jika Anda menggunakan rutin interupsi ADC untuk menetapkan nilai baru, yang disebut ketika nilai ADC baru siap. Dalam hal itu, kompiler tidak akan memiliki petunjuk tentang kapan ISR yang sesuai dipanggil dan dapat memutuskan bahwa adcValue tidak akan diakses dengan cara ini. Di sinilah volatile akan membantu.
sumber
Perilaku
volatile
argumen sangat tergantung pada kode Anda, kompiler, dan optimasi yang dilakukan.Ada dua kasus penggunaan yang saya gunakan secara pribadi
volatile
:Jika ada variabel yang ingin saya lihat dengan debugger, tetapi kompiler telah mengoptimalkannya (berarti telah menghapusnya karena ternyata tidak perlu memiliki variabel ini), menambahkan
volatile
akan memaksa kompiler untuk menyimpannya dan karenanya dapat dilihat pada debug.Jika variabel dapat berubah "di luar kode", biasanya jika Anda memiliki beberapa perangkat keras yang mengaksesnya, atau jika Anda memetakan variabel secara langsung ke alamat.
Dalam embedded juga kadang-kadang ada beberapa bug di kompiler, melakukan optimasi yang sebenarnya tidak berfungsi, dan kadang-kadang
volatile
bisa menyelesaikan masalah.Mengingat Anda variabel Anda dideklarasikan secara global, itu mungkin tidak akan dioptimalkan, selama variabel sedang digunakan pada kode, setidaknya ditulis dan dibaca.
Contoh:
Dalam hal ini, variabel mungkin akan dioptimalkan untuk printf ("% i", 1);
tidak akan dioptimalkan
Yang lainnya:
Dalam hal ini, kompiler dapat mengoptimalkan dengan (jika Anda mengoptimalkan kecepatan) dan dengan demikian membuang variabel
Untuk kasus penggunaan Anda, "itu mungkin tergantung" pada sisa kode Anda, bagaimana
adcValue
digunakan di tempat lain dan pengaturan versi / optimisasi yang Anda gunakan.Terkadang menjengkelkan memiliki kode yang berfungsi tanpa optimasi, tetapi rusak begitu dioptimalkan.
Ini mungkin dioptimalkan untuk printf ("% i", readADC ());
-
Ini mungkin tidak akan dioptimalkan, tetapi Anda tidak pernah tahu "seberapa bagus kompilernya" dan mungkin berubah dengan parameter kompiler. Biasanya kompiler dengan optimasi yang baik dilisensikan.
sumber
volatile
memaksa kompiler untuk menyimpan variabel dalam RAM, dan memperbarui RAM itu segera setelah nilai ditugaskan ke variabel. Sebagian besar waktu, kompiler tidak 'menghapus' variabel, karena kita biasanya tidak menulis tugas tanpa efek, tetapi mungkin memutuskan untuk menyimpan variabel dalam beberapa register CPU dan kemudian atau tidak pernah menulis nilai register itu ke RAM. Debugger sering gagal menemukan register CPU tempat variabel disimpan dan karenanya tidak dapat menunjukkan nilainya.Banyak penjelasan teknis tetapi saya ingin berkonsentrasi pada aplikasi praktis.
Kata
volatile
kunci memaksa kompiler untuk membaca atau menulis nilai variabel dari memori setiap kali digunakan. Biasanya kompiler akan mencoba untuk mengoptimalkan tetapi tidak membuat membaca dan menulis yang tidak perlu, misalnya dengan menyimpan nilai dalam register CPU daripada mengakses memori setiap kali.Ini memiliki dua kegunaan utama dalam kode tertanam. Pertama digunakan untuk register perangkat keras. Register perangkat keras dapat berubah, mis. Register hasil ADC dapat ditulis oleh perangkat ADC. Register perangkat keras juga dapat melakukan tindakan saat diakses. Contoh umum adalah register data UART, yang sering membersihkan tanda interupsi ketika dibaca.
Kompiler biasanya akan mencoba untuk mengoptimalkan membaca berulang dan menulis register dengan asumsi bahwa nilai tidak akan pernah berubah sehingga tidak perlu terus mengaksesnya, tetapi
volatile
kata kunci akan memaksanya untuk melakukan operasi baca setiap kali.Penggunaan umum kedua adalah untuk variabel yang digunakan oleh kode interrupt dan non-interrupt. Interupsi tidak dipanggil secara langsung, sehingga kompiler tidak dapat menentukan kapan mereka akan mengeksekusi, dan dengan demikian mengasumsikan bahwa setiap akses di dalam interupsi tidak pernah terjadi. Karena
volatile
kata kunci memaksa kompiler untuk mengakses variabel setiap saat, asumsi ini dihapus.Penting untuk dicatat bahwa
volatile
kata kunci bukan solusi lengkap untuk masalah ini, dan harus berhati-hati untuk menghindarinya. Sebagai contoh, pada sistem 8 bit variabel 16 bit memerlukan dua akses memori untuk membaca atau menulis, dan bahkan jika kompiler dipaksa untuk membuat akses tersebut terjadi secara berurutan, dan dimungkinkan untuk perangkat keras untuk bertindak pada akses pertama atau interupsi terjadi antara keduanya.sumber
Dengan tidak adanya
volatile
kualifikasi, nilai objek dapat disimpan di lebih dari satu tempat selama bagian kode tertentu. Pertimbangkan, misalnya, diberikan sesuatu seperti:Pada hari-hari awal C, kompiler akan memproses pernyataan itu
melalui langkah-langkah:
Kompiler yang lebih canggih, bagaimanapun, akan mengakui bahwa jika nilai "foo" disimpan dalam register selama loop, itu hanya perlu dimuat sekali sebelum loop, dan disimpan sekali setelah. Namun, selama loop, itu berarti bahwa nilai "foo" disimpan di dua tempat - di dalam penyimpanan global, dan di dalam register. Ini tidak akan menjadi masalah jika kompilator dapat melihat semua cara "foo" dapat diakses dalam loop, tetapi dapat menyebabkan masalah jika nilai "foo" diakses dalam beberapa mekanisme yang tidak diketahui kompilator ( seperti interrupt handler).
Mungkin ada kemungkinan bagi penulis Standar untuk menambahkan kualifikasi baru yang secara eksplisit akan mengundang kompiler untuk melakukan optimasi seperti itu, dan mengatakan bahwa semantik kuno akan berlaku jika tidak ada, tetapi kasus-kasus di mana optimasi sangat bermanfaat melebihi jumlah yang mana hal itu akan menimbulkan masalah, sehingga Standar malah memungkinkan penyusun untuk berasumsi bahwa optimisasi seperti itu aman tanpa adanya bukti bahwa mereka tidak. Tujuan
volatile
kata kunci adalah untuk menyediakan bukti tersebut.Beberapa titik pertentangan antara beberapa penulis kompiler dan pemrogram terjadi dengan situasi seperti:
Secara historis, sebagian besar kompiler akan memungkinkan untuk kemungkinan bahwa menulis
volatile
lokasi penyimpanan dapat memicu efek samping yang sewenang-wenang, dan menghindari caching nilai apa pun di register di toko tersebut, atau mereka akan menahan diri dari caching nilai dalam register di seluruh panggilan ke fungsi yang tidak memenuhi syarat "inline", dan dengan demikian akan menulis 0x1234 keoutput_buffer[0]
, mengatur hal-hal untuk menampilkan data, menunggu untuk menyelesaikan, kemudian menulis 0x2345 untukoutput_buffer[0]
, dan melanjutkan dari sana. Standar tidak memerlukan implementasi untuk memperlakukan tindakan menyimpan alamatoutput_buffer
menjadi avolatile
-Penunjuk yang memenuhi syarat sebagai tanda bahwa sesuatu mungkin terjadi padanya melalui berarti kompiler tidak mengerti, namun, karena penulis pikir kompiler penulis kompiler yang ditujukan untuk berbagai platform dan tujuan akan mengenali ketika melakukan itu akan melayani tujuan-tujuan tersebut pada platform tersebut tanpa harus diberi tahu. Akibatnya, beberapa kompiler "pintar" seperti gcc dan dentang akan menganggap bahwa meskipun alamatoutput_buffer
ditulis ke pointer yang memenuhi syarat volatile antara dua tokooutput_buffer[0]
, itu bukan alasan untuk menganggap bahwa apa pun mungkin peduli tentang nilai yang dipegang pada objek di waktu itu.Lebih lanjut, sementara pointer yang dilemparkan langsung dari bilangan bulat jarang digunakan untuk tujuan apa pun selain untuk memanipulasi hal-hal dengan cara yang tidak mungkin dipahami oleh penyusun, Standar lagi tidak mengharuskan penyusun untuk memperlakukan akses semacam itu sebagai
volatile
. Akibatnya, penulisan pertama*((unsigned short*)0xC0001234)
dapat dihilangkan oleh kompiler "pintar" seperti gcc dan dentang, karena pengelola kompiler semacam itu lebih suka mengklaim bahwa kode yang mengabaikan kualifikasi hal-hal sepertivolatile
"rusak" daripada mengakui bahwa kompatibilitas dengan kode seperti itu berguna . Banyak file header yang disediakan vendor menghilangkanvolatile
kualifikasi, dan kompiler yang kompatibel dengan file header yang disediakan vendor lebih berguna daripada yang tidak.sumber