Mengapa bahasa pemrograman C masih menguasai

Tidak ada teknologi yang bertahan selama 50 tahun kecuali jika ia melakukan tugasnya lebih baik daripada kebanyakan teknologi lainnya — terutama teknologi komputer. Bahasa pemrograman C telah hidup dan populer sejak 1972, dan masih berkuasa sebagai salah satu blok bangunan fundamental dari dunia yang ditentukan perangkat lunak kami.

Tetapi terkadang sebuah teknologi bertahan karena orang-orang belum sempat menggantinya. Selama beberapa dekade terakhir, lusinan bahasa lain telah muncul — beberapa secara eksplisit dirancang untuk menantang dominasi C, beberapa memotong C dari samping sebagai produk sampingan dari popularitas mereka.

Tidak sulit untuk membantah bahwa C perlu diganti. Penelitian bahasa pemrograman dan praktik pengembangan perangkat lunak semuanya mengisyaratkan bagaimana ada cara yang jauh lebih baik untuk melakukan sesuatu daripada cara C. Tapi C tetap sama, dengan penelitian dan pengembangan puluhan tahun di belakangnya. Beberapa bahasa lain dapat mengalahkannya untuk performa, kompatibilitas bare-metal, atau di mana-mana. Tetap saja, ada baiknya melihat bagaimana C dibandingkan dengan kompetisi bahasa terkenal di tahun 2018.

C vs. C ++

Biasanya, C paling sering dibandingkan dengan C ++, bahasa yang — seperti yang ditunjukkan oleh namanya sendiri — dibuat sebagai perpanjangan dari C. Perbedaan antara C ++ dan C dapat dicirikan sebagai ekstensif, atau  berlebihan , tergantung pada siapa Anda bertanya.

Meskipun masih mirip C dalam sintaks dan pendekatannya, C ++ menyediakan banyak fitur yang benar-benar berguna yang tidak tersedia secara native di C: namespace, template, pengecualian, manajemen memori otomatis, dan sebagainya. Proyek yang menuntut kinerja tingkat atas — database, sistem pembelajaran mesin — sering kali ditulis dalam C ++ menggunakan fitur tersebut untuk memeras setiap penurunan kinerja dari sistem.

Lebih lanjut, C ++ terus berkembang jauh lebih agresif daripada C. C ++ 20 yang akan datang membawa lebih banyak lagi ke tabel termasuk modul, coroutine, pustaka sinkronisasi, dan konsep, yang membuat template lebih mudah digunakan. Revisi terbaru untuk standar C menambahkan sedikit dan berfokus pada mempertahankan kompatibilitas ke belakang.

Masalahnya, semua plus di C ++ juga bisa berfungsi sebagai minus. Yang besar. Semakin banyak fitur C ++ yang Anda gunakan, semakin kompleks Anda memperkenalkan dan semakin sulit untuk menjinakkan hasilnya. Pengembang yang membatasi diri pada subset C ++ dapat menghindari banyak perangkap dan kelebihan terburuknya. Tetapi beberapa toko ingin melindungi dari kompleksitas C ++ secara bersamaan. Tetap menggunakan C memaksa developer untuk membatasi diri pada subset tersebut. Tim pengembangan kernel Linux, misalnya, menghindari C ++.

Memilih C daripada C ++ adalah cara bagi Anda — dan setiap pengembang yang mengelola kode setelah Anda — untuk menghindari masalah dengan kelebihan C ++, dengan menerapkan gaya minimalis yang dipaksakan. Tentu saja, C ++ memiliki sekumpulan fitur tingkat tinggi yang kaya untuk alasan yang baik. Tetapi jika minimalis lebih cocok untuk proyek saat ini dan masa depan — dan tim proyek — maka C lebih masuk akal.

C vs. Jawa

Setelah beberapa dekade, Java tetap menjadi bahan pokok pengembangan perangkat lunak perusahaan — dan pokok pengembangan secara umum. Banyak proyek perangkat lunak perusahaan yang paling signifikan ditulis di Java — termasuk sebagian besar proyek Apache Software Foundation — dan Java tetap menjadi bahasa yang layak untuk mengembangkan proyek baru dengan persyaratan tingkat perusahaan.

Sintaks Java meminjam banyak dari C dan C ++. Tidak seperti C, Java tidak secara default dikompilasi ke kode native. Sebaliknya, lingkungan runtime Java, JVM, JIT (just-in-time) mengkompilasi kode Java untuk dijalankan di lingkungan target. Dalam keadaan yang tepat, kode Java JITted dapat mendekati atau bahkan melebihi kinerja C.

Filosofi "tulis sekali, jalankan di mana saja" di belakang Java juga memungkinkan program Java untuk berjalan dengan sedikit penyesuaian untuk arsitektur target. Sebaliknya, meskipun C telah di-porting ke banyak arsitektur, program C tertentu mungkin masih memerlukan kustomisasi untuk berjalan dengan baik, katakanlah, Windows versus Linux.

Kombinasi portabilitas dan kinerja yang kuat ini, bersama dengan ekosistem besar perpustakaan perangkat lunak dan kerangka kerja, menjadikan Java sebagai bahasa dan waktu proses masuk untuk membangun aplikasi perusahaan.

Di mana Java kekurangan C adalah area di mana Java tidak pernah dimaksudkan untuk bersaing: mendekati logam, atau bekerja langsung dengan perangkat keras. Kode C dikompilasi menjadi kode mesin, yang dijalankan oleh proses secara langsung. Java dikompilasi menjadi bytecode, yang merupakan kode perantara yang kemudian diubah oleh juru bahasa JVM menjadi kode mesin. Lebih lanjut, meskipun manajemen memori otomatis Java adalah berkah dalam banyak situasi, C lebih cocok untuk program yang harus mengoptimalkan penggunaan sumber daya memori terbatas.

Konon, ada beberapa daerah di mana Jawa bisa mendekati C dalam hal kecepatan. Mesin JIT JVM mengoptimalkan rutinitas saat runtime berdasarkan perilaku program, memungkinkan banyak kelas pengoptimalan yang tidak mungkin dilakukan dengan C yang dikompilasi sebelumnya. Dan sementara runtime Java mengotomatiskan manajemen memori, beberapa aplikasi baru mengatasinya. Misalnya, Apache Spark mengoptimalkan sebagian pemrosesan dalam memori dengan menggunakan kode manajemen memori khusus yang mengakali JVM.

C vs. C # dan .Net

Hampir dua dekade setelah diperkenalkan, C # dan .Net Framework tetap menjadi bagian utama dunia perangkat lunak perusahaan. Dikatakan bahwa C # dan .Net adalah respons Microsoft terhadap Java — sistem kompilator kode terkelola dan runtime universal — dan begitu banyak perbandingan antara C dan Java juga berlaku untuk C dan C # /. Net.

Seperti Java (dan Python sampai batas tertentu), .Net menawarkan portabilitas di berbagai platform dan ekosistem perangkat lunak terintegrasi yang luas. Ini bukanlah keuntungan kecil mengingat seberapa banyak pengembangan berorientasi perusahaan terjadi di dunia .Net. Saat Anda mengembangkan program dalam C #, atau bahasa .Net lainnya, Anda dapat menggunakan berbagai alat dan pustaka yang ditulis untuk waktu proses .Net. 

Keunggulan .NET mirip Java lainnya adalah pengoptimalan JIT. Program C # dan .Net dapat dikompilasi sebelumnya sesuai C, tetapi mereka terutama dikompilasi oleh waktu-waktu .Net runtime dan dioptimalkan dengan informasi runtime. Kompilasi JIT memungkinkan semua jenis pengoptimalan di tempat untuk program .Net yang sedang berjalan yang tidak dapat dilakukan di C.

Seperti C, C # dan .Net menyediakan berbagai mekanisme untuk mengakses memori secara langsung. Heap, stack, dan memori sistem yang tidak dikelola semuanya dapat diakses melalui API dan objek .Net. Dan pengembang dapat menggunakan unsafemode dalam .Net untuk mencapai kinerja yang lebih baik.

Namun, tidak ada yang datang secara gratis. Objek dan objek yang dikelola unsafetidak dapat dipertukarkan secara sewenang-wenang, dan pengaturan di antara keduanya menimbulkan biaya performa. Oleh karena itu, memaksimalkan kinerja aplikasi .Net berarti meminimalkan pergerakan antara objek yang dikelola dan tidak dikelola.

Ketika Anda tidak mampu membayar penalti untuk memori yang dikelola vs. tidak terkelola, atau ketika runtime .Net adalah pilihan yang buruk untuk lingkungan target (misalnya, ruang kernel) atau mungkin tidak tersedia sama sekali, maka C adalah yang Anda perlu. Dan tidak seperti C # dan .Net, C membuka akses memori langsung secara default. 

C vs. Pergi

Sintaks Go berutang banyak pada C — kurung kurawal sebagai pembatas, pernyataan diakhiri dengan titik koma, dan seterusnya. Pengembang yang mahir dalam C biasanya dapat langsung beralih ke Go tanpa banyak kesulitan, bahkan dengan mempertimbangkan fitur Go baru seperti ruang nama dan manajemen paket.

Kode yang dapat dibaca adalah salah satu dari tujuan desain panduan Go: Memudahkan pengembang untuk mempercepat setiap proyek Go dan menjadi mahir dengan basis kode dalam waktu singkat. Basis kode C bisa jadi sulit untuk dipahami, karena mereka cenderung berubah menjadi sarang tikus makro dan #ifdefspesifik untuk proyek dan tim tertentu. Sintaks Go, dan pemformatan kode bawaan serta alat manajemen proyek, dimaksudkan untuk mencegah masalah kelembagaan semacam itu.

Go juga memiliki fitur ekstra seperti goroutine dan saluran, alat tingkat bahasa untuk menangani konkurensi dan pesan yang lewat antar komponen. C akan membutuhkan hal-hal seperti itu untuk digulung tangan atau disediakan oleh perpustakaan eksternal, tetapi Go menyediakannya langsung dari kotak, membuatnya jauh lebih mudah untuk membangun perangkat lunak yang membutuhkannya.

Di mana Go paling berbeda dari C di balik terpal adalah dalam manajemen memori. Objek Go secara otomatis dikelola dan dikumpulkan sampah secara default. Untuk sebagian besar pekerjaan pemrograman, ini sangat nyaman. Tetapi itu juga berarti bahwa program apa pun yang membutuhkan penanganan memori deterministik akan lebih sulit untuk ditulis.

Go memang menyertakan unsafepaket untuk menghindari beberapa keamanan penanganan tipe Go, seperti membaca dan menulis memori sembarang dengan sebuah Pointertipe. Namun unsafedilengkapi dengan peringatan bahwa program yang ditulis dengannya "mungkin tidak portabel dan tidak dilindungi oleh pedoman kompatibilitas Go 1."

Go sangat cocok untuk membangun program seperti utilitas baris perintah dan layanan jaringan, karena mereka jarang membutuhkan manipulasi yang begitu mendetail. Tetapi driver perangkat tingkat rendah, komponen sistem operasi ruang kernel, dan tugas lain yang menuntut kontrol ketat atas tata letak dan manajemen memori paling baik dibuat di C.

C vs. Rust

Dalam beberapa hal, Rust adalah respons terhadap teka-teki manajemen memori yang dibuat oleh C dan C ++, dan juga banyak kekurangan lain dari bahasa ini. Rust mengkompilasi ke kode mesin asli, sehingga dianggap setara dengan C dalam hal kinerja. Keamanan memori secara default, bagaimanapun, adalah nilai jual utama Rust.

Aturan kompilasi dan sintaksis Rust membantu pengembang menghindari kesalahan umum manajemen memori. Jika sebuah program memiliki masalah manajemen memori yang melintasi sintaks Rust, itu tidak akan bisa dikompilasi. Pendatang baru bahasa ini, terutama dari bahasa seperti C yang menyediakan banyak ruang untuk bug semacam itu, menghabiskan fase pertama pendidikan Rust mereka untuk mempelajari cara menenangkan kompilator. Tapi pendukung Rust berpendapat bahwa rasa sakit jangka pendek ini memiliki hasil jangka panjang: kode yang lebih aman yang tidak mengorbankan kecepatan.

Rust juga memperbaiki C dengan perkakasnya. Manajemen proyek dan komponen adalah bagian dari toolchain yang disediakan Rust secara default, sama seperti Go. Ada cara default yang disarankan untuk mengelola paket, mengatur folder proyek, dan menangani banyak hal hebat lainnya yang di C paling baik ad-hoc, dengan setiap proyek dan tim menanganinya secara berbeda.

Namun, apa yang disebut-sebut sebagai keunggulan di Rust mungkin tidak tampak seperti salah satu pengembang C. Fitur keamanan waktu kompilasi Rust tidak dapat dinonaktifkan, jadi program Rust yang paling sepele sekalipun harus sesuai dengan batasan keamanan memori Rust. C mungkin kurang aman secara default, tetapi jauh lebih fleksibel dan memaafkan jika diperlukan.

Kelemahan lain yang mungkin adalah ukuran bahasa Rust. C memiliki fitur yang relatif sedikit, bahkan ketika mempertimbangkan perpustakaan standar. Set fitur Rust meluas dan terus berkembang. Seperti dengan C ++, set fitur Rust yang lebih besar berarti lebih banyak daya, tetapi juga lebih banyak kerumitan. C adalah bahasa yang lebih kecil, tapi lebih mudah untuk dimodelkan secara mental, jadi mungkin lebih cocok untuk proyek di mana Rust akan berlebihan.

C vs. Python

Hari-hari ini, setiap kali pembicaraan tentang pengembangan perangkat lunak, Python sepertinya selalu memasuki percakapan. Bagaimanapun, Python adalah "bahasa terbaik kedua untuk semuanya", dan tidak diragukan lagi salah satu yang paling serbaguna, dengan ribuan pustaka pihak ketiga yang tersedia.

Apa yang ditekankan Python, dan yang paling berbeda dari C, lebih mengutamakan kecepatan pengembangan daripada kecepatan eksekusi. Sebuah program yang mungkin membutuhkan satu jam untuk disatukan dalam bahasa lain — seperti C — mungkin dirakit dengan Python dalam hitungan menit. Di sisi lain, program itu mungkin memerlukan beberapa detik untuk dijalankan di C, tapi satu menit untuk dijalankan dengan Python. (Aturan praktis yang baik: Program Python umumnya menjalankan urutan besarnya lebih lambat daripada rekan C mereka.) Tetapi untuk banyak pekerjaan pada perangkat keras modern, Python cukup cepat, dan itu telah menjadi kunci untuk penyerapannya.

Perbedaan utama lainnya adalah manajemen memori. Program Python sepenuhnya dikelola memori oleh runtime Python, jadi pengembang tidak perlu khawatir tentang seluk beluk mengalokasikan dan membebaskan memori. Tapi di sini sekali lagi, kemudahan pengembang datang dengan mengorbankan kinerja runtime. Penulisan program C membutuhkan perhatian yang cermat pada manajemen memori, tetapi program yang dihasilkan sering kali menjadi standar emas untuk kecepatan mesin murni.

Di bawah kulit, Python dan C berbagi koneksi yang dalam: referensi runtime Python ditulis dalam C. Hal ini memungkinkan program Python untuk membungkus pustaka yang ditulis dalam C dan C ++. Bagian signifikan dari ekosistem Python pustaka pihak ketiga, seperti untuk pembelajaran mesin, memiliki kode C sebagai intinya.

Jika kecepatan pengembangan lebih penting daripada kecepatan eksekusi, dan jika sebagian besar bagian yang berkinerja dari program dapat diisolasi menjadi komponen mandiri (sebagai lawan disebarkan ke seluruh kode), baik Python murni atau campuran pustaka Python dan C. pilihan yang lebih baik daripada C saja. Jika tidak, C masih berkuasa.