Artikel ini bermaksud menjelaskan perbedaan berbagai jenis compiler C, library C, dan juga bagaimana kita bisa memakai SDK (software development kit) untuk suatu platform dengan Compiler C alternatif.
Daftar Isi
Compiler C
Tugas compiler C adalah menerjemahkan kode dalam bahasa C ke assembly. Kebanyakan compiler bisa membuat file teks .s (source assembly) yang kemudian bisa dicompile dengan assembler menjadi binary. Sebagian compiler langsung menghasilkan kode biner.

Ketika memprogram microcontroller atau sistem lain yang tidak memakai sistem operasi, kita cuma perlu mengubah alamat memori tertentu untuk melakukan input/output (jika CPUnya mendukung memory-mapped I/O), atau menggunakan instruksi I/O khusus. Sistem semacam ini sebut dengan sistem “freestanding”. Di sistem semacam ini bahkan tidak wajib ada fungsi bernama “main”, programmer yang mendefinisikan fungsi mana yang harus dimulai. Di sistem freestanding, kita bisa membuat program TANPA library C sama sekali.

Jika kita menjalankan program C di desktop/mobile atau sistem lain yang memakai sistem operasi, istilahnya kita memakai sistem “hosted”. Di sini ada konvensi yang sudah ditetapkan, misalnya program dimulai di fungsi bernama “main”.
Ada banyak compiler C saat ini, yang paling terkenal saat ini adalah:
- GCC (Gnu Compiler Collection)
- Clang
- MSVC (Microsoft Visual C, hanya untuk Windows)
Tapi masih banyak yang lain yang juga bisa dipakai, misalnya:
- TCC (tiny C compiler)
- LCC (Local/Little C Compiler)
- SDCC (Small Device C Compiler)
- Watcom C++
Apa bedanya antar compiler-compiler ini?
- Lisensi: ada yg komersial (MSVC), ada yang open source (GCC memiliki lisensi GPL, Clang memiliki lisensi Apache2)
- Sistem operasi yang disupport (MSVC hanya Windows, GCC dan Clang sifatnya cross platform)
- CPU yang disupport
- Standard bahasa C yang didukung (beberapa compiler seperti LCC tidak mendukung standard C terbaru)
- Optimasi yang bisa dilakukan
- Fitur ekstra yang tidak ada di standard C (misalnya GCC mendukung fungsi bersarang)
Khusus untuk Clang vs GCC, meski keduanya open source, lisensi Clang disukai oleh pihak komersial (AMD, Apple, dsb) karena perbedaan dasar lisensi:
- Jika kita membuat perubahan di GCC, lalu merilis executablenya, maka source codenya HARUS dirilis
- Jika kita membuat perubahan di Clang, lalu merilis executablenya, maka source codenya boleh tetap dirahasiakan
Cross Compiler
Cross compiler adalah compiler yang bisa menghasilkan assembly untuk target lain, misalnya compiler C di Windows Intel menghasilkan binary untuk Linux Intel, atau bahkan Linux ARM.
Compiler GCC perlu dikompilasi khusus untuk mendukung cross compilation, tapi Clang lebih mudah. Perlu diperhatikan bahwa hanya bagian COMPILE saja yang mudah, bagian linking dan pemakaian library tetap tidak mudah.
Library C Standard
Ketika kita memanggil “strlen”, “printf”, “puts”, “gets”, “open”, dsb, itu adalah fungsi bawaan library C standard. Jadi dalam contoh kecil ini: compiler akan mengoutputkan kode “call puts” (panggil fungsi “puts”), tapi bukan tanggung jawab compiler apa isi dari fungsi “puts” yang dipanggil ini. Asumsinya sudah ada yang mengimplementasikan ini, untuk fungsi standard, ini ada di library C.

Bagaimana ini diimplementasikan, terserah pembuat library C-nya. Ada banyak library C, misalnya:
- GLIBC
- uClibc
- MUSL
- Library dari MSVC (karena masalah kompatibilitas, ini agak rumit, akan dijelaskan lebih lanjut)
Perbedaan utama library C adalah:
- Kecepatannya
- Support terhadap sistem operasi
Banyak kode library C bisa kita tulis sendiri dengan mudah, misalnya strlen sederhana implementasinya seperti ini:
size_t strlen(const char *str) {
size_t len = 0;
while (str[len]) {
len++;
}
return len;
}
Sedangkan jika kita bandingkan dengan library lain:
- Glibc memakai manipulasi bit: https://github.com/lattera/glibc/blob/master/string/strlen.c
- Uclibc memakai kode assembly, misalnya ini untuk arm: https://github.com/kraj/uClibc/blob/master/libc/string/arm/strlen.S
- Musl memakai beberapa macro: https://git.musl-libc.org/cgit/musl/tree/src/string/strlen.c
Walau semuanya akan menghasilkan nilai yang sama tapi kecepatannya akan berbeda.

Perbedaan berikutnya dari berbagai library C ini adalah: support sistem operasi. Di Linux jika kita ingin membuka file, maka kita perlu memakai syscall (dengan instruksi syscall
di assembly) dengan nomor tertentu (seperti pernah saya bahas di artikel ini). Syscal ini sangat bergantung pada sistem operasi. Di macOS, bahkan tidak dijamin syscall ini akan tetap sama di versi OS yang baru, jadi semua program perlu memanggil library milik sistem.
Perhatikan: C library ini dibutuhkan untuk platform target, jadi jika kita ingin mengkompilasi kode di Windows untuk tujuan Linux, maka kita perlu library C untuk Linux. Jika kita ingin mengcompile untuk hardware lain (misalnya sistem point of sale), maka kita perlu memiliki library untuk target.
Library statik dan dinamik/shared
Ini sudah pernah saya bahas lebih dalam di sini. Singkatnya adalah:
- Pada static linking, kode program dari library dicopy ke dalam executable, jadi ketika runtime (ketika program berjalan), tidak perlu library (.so atau .dll). Kelebihan: tidak butuh library tambahan, kelemahan: ukuran program jadi besar, jika ada bug di library, semua program harus dicompile ulang.
- Pada dynamic linking: kode library diload saat runtime, jadi library ini dibutuhkan ketika program berjalan. Kelebihan: program lebih kecil, jika ada bug di library, cukup update librarynya. Kekurangan: bisa ada bentrok antar library, butuh library yang sesuai ketika program berjalan.
Istilah untuk library yang dibutuhkan ketika program berjalan adalah: runtime library
Library C di Windows
Windows sudah ada sejak lama, dan memiliki library C standard untuk runtime, yaitu MSVCRT. Sampai sekarang pun, MSVCRT ini masih ada dan didukung, tapi memiliki masalah: library ini sudah tua, tidak mendukung fitur C99 ke atas dan tidak mendukung UTF-8. Untuk mengatasi itu ada yang namanya UCRT (Universal C Runtime), ini library yang baru, dan kompatibel dengan Windows baru (Windows 10 dan Windows 2016 ke atas).
MingW
Sebagai solusi, ada distribusi compiler GCC untuk Windows yang bernama MingW. MingW memiliki library C yang memakai library milik Windows, tapi menambahkan fungsi-fungsi yang dibutuhkan. MingW hanya menambahkan fungsi yang hilang, jadi sisanya tetap harus ditangani baik dengan MSVCRT atau UCRT.
Intinya: jika masih menggunakan Windows lama, pakailah versi MSVCRT. Jika menargetkan Windows baru, gunakan UCRT. MingW akan membantu menambahkan fungsi yang mungkin tidak tersedia.
Cygwin
Cygwin memiliki pendekatan yang berbeda: memakai library C-nya sendiri, dan tujuannya membuat program yang ditulis untuk Linux/Unix bisa berjalan di Windows. Semua hal diterjemahkan, misalnya namafile di Windows memakai drive dan path memakai backslash, sedangkan di Linux/Unix tidak ada drive dan memakai slash. Cygwin akan menerjemahkan semua path agar seperti di Linux. Selain nama file, berbagai hal spesifik Linux juga diterjemahkan.
SDK (software development kit)
Ketika ingin memprogram sebuah device, kita perlu memakai SDK dari vendor, ini biasanya terdiri dari:
- Compiler C
- Assember dan Linker
- Library C standard
- Library khusus hardware (dari vendor)
- Library generik (misalnya zlib untuk kompresi, jpeg untuk decode JPG, dsb)
Seperti bisa diduga: library milik vendor ini akan memakai library C standard sesuai yang dipakai oleh vendor tersebut. Kalau vendornya memakai uClibc, maka library hardware dari vendor akan perlu uClibC.
Built in functions dan ABI support functions
Agar program bisa berjalan sangat cepat, apa yang saya tuliskan di atas tidak sepenuhnya benar. Beberapa compiler punya fungsi built in, dan kadang bisa menghasilkan kode tertentu tanpa memakai library C.
Berikut ini contoh sangat sederhana:
#include <string.h>
void hello(char *input) {
memset(input, 0, 4);
}
Jika compiler diminta mengcompile kode itu, tanpa optimasi, hasilnya sesuai harapan, yaitu memanggil fungsi memset di library C:

Tapi jika dioptimasi, compiler mengetahui bahwa: ini hanya perlu mengoverwite 4 byte dengan angka 0, dan ada satu instruksi saja yang bisa menggantikan pemanggilan fungsinya:

Intinya adalah: compiler bisa memiliki fungsi bawaan (istilahnya ini adalah : builtins).
Selain builtins, compiler bisa memiliki ABI support functions. Tidak semua prosessor memiliki instruksi yang sama (terutama dalam embedded system), contohnya prosessor lama tidak punya instruksi DIV di hardware, atau punya tapi hanya untuk 32 bit saja. Untuk mengakali ini, jika ada div, maka compiler akan mengoutputkan kode yang memanggil fungsi pembagian.
#include <stdint.h>
uint32_t div32 ( uint32_t a, uint32_t b )
{
return(a/b);
}
uint64_t div64 ( uint64_t a, uint64_t b )
{
return(a/b);
}
Berikut ini contoh untuk prosessor ARM lama yang memanggil _aeabi_udivmod jika ada pembagian bilangan 64 bit.

Fungsi ini bukan dari standard C, tapi standard untuk Application Binary Interface untuk platform tersebut. Sederhananya: ini merupakan library ekstra yang dimiliki oleh sebuah compiler.
Kombinasi Compiler dan Library
Jika kita memiliki kode program dari satu compiler, apakah bisa dipakai di compiler lain? jawabannya: tergantung banyak hal. Secara umum, kode yang memenuhi standard ABI bisa dicampur-campur, misalnya seperti ini.

Default Path
Tiap compiler memiliki default path yang berbeda-beda. Ini berisi informasi: di mana harus mencari header dan library. Jika dua compiler memakai library yang berbeda, ini kemungkinan menyebabkan bentrok (gagal compile) atau bahkan error ketika dijalankan.
Untuk GCC, kita bisa melihat aneka path ini dengan gcc -v
, contoh outputnya:
yohanes@eleven ~ $ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/12/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 12.2.0-14' --with-bugurl=file:///usr/share/doc/gcc-12/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-12 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-12-bTRWOB/gcc-12-12.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-12-bTRWOB/gcc-12-12.2.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 12.2.0 (Debian 12.2.0-14)
Sedangkan untuk clang, kita perlu opsi clang -E -x c -v /dev/null
.
Tips mengganti compiler
Jika kita ingin menggantikan GCC dengan Clang, atau Clang dengan GCC, akan lebih baik dan lebih mudah jika compiler penggantinya dikompilasi dengan compiler yang ingin kita gantikan.
Contohnya: kita memakai MingW GCC, dan ingin memakai Clang (misalnya karena clang-nya sudah dimodifikasi), kita bisa mengkompilasi Clang dengan GCC dari MingW. Hasilnya: segala macam path dan library yang dipakai oleh GCC akan dipakai juga oleh Clang, dan lebih mudah menggunakan Clang sebagai pengganti GCC
Demikian juga jika ingin menggantikan MSVC (compiler C dari microsoft) dengan Clang. Compile Clang dengan MSVC. Khusus untuk MSVC, clang juga memiliki front end clang-cl.exe, yang berusaha mengcopy semua parameter dan flag yang diterima oleh cl.exe (compiler milik MSVC).
Penutup
Berurusan dengan berbagai compiler C memang kadang sulit, semoga artikel ini bisa membantu memperjelas pemahaman pembaca mengenai compiler dan library. Jika sudah paham ini, melakukan cross-compiling, atau mengganti satu compiler dengan yang lain bisa dilakukan walau tetap tidak mudah.