Penulis kode C tidak hanya berkomunikasi dengan compiler, tetapi juga dengan manusia yang akan membaca kode enam bulan ke depan—termasuk diri sendiri. Compiler mengabaikan indentasi, spasi, dan posisi kurung kurawal ({}); manusia tidak. Bab ini membandingkan tiga brace style yang paling banyak dipakai di komunitas C (K&R, Allman, dan Linux kernel), menunjukkan kapan tab vs space menjadi pertimbangan penting, lalu memperkenalkan alat otomatis seperti clang-format yang menjaga konsistensi format di seluruh tim. Pada akhir bab ini kita akan mampu memilih satu gaya, mempertahankannya secara konsisten, dan tahu kapan harus mengikuti gaya proyek yang sudah ada.
Mengapa Formatting Penting #
Compiler C mengabaikan whitespace. Tiga fragmen berikut menghasilkan bytecode identik:
int hitung(int x){return x*2;}int hitung(int x) {
return x * 2;
}int hitung(int x)
{
return x * 2;
}Meskipun compiler memperlakukan ketiganya sama, mata manusia membutuhkan struktur visual yang konsisten untuk memindai logika dengan cepat. Studi mata (eye-tracking) yang dirilis oleh Software Quality Journal menunjukkan bahwa developer membaca kode terformat rata kiri-kanan ~30% lebih cepat daripada kode yang indentasinya tidak konsisten. Inilah mengapa standar seperti Linux kernel coding style, GNU Coding Standards, dan CERT C memiliki aturan eksplisit tentang indentasi dan brace placement.
Tiga Brace Style Utama #
K&R / 1TBS (One True Brace Style) #
Gaya K&R dipopulerkan oleh buku The C Programming Language oleh Kernighan dan Ritchie. Ciri khasnya: kurung kurawal pembuka ({) berada di akhir baris yang sama dengan keyword if, for, while, atau deklarasi fungsi. Single-statement if tanpa brace kadang dibiarkan tanpa brace, tetapi CERT C dan MISRA C menyarankan selalu memakai brace.
/* File: format1.c - K&R / 1TBS style */
#include <stdio.h>
static int hitung_diskon(int harga, int persen) {
if (persen < 0 || persen > 100) {
return -1;
}
int potongan = harga * persen / 100;
return harga - potongan;
}
int main(void) {
int harga = 200000;
int persen = 25;
int akhir = hitung_diskon(harga, persen);
if (akhir < 0) {
fprintf(stderr, "persen tidak valid\n");
return 1;
}
printf("Harga awal : %d\n", harga);
printf("Diskon %d%%: %d\n", persen, harga - akhir);
printf("Harga akhir: %d\n", akhir);
return 0;
}Output yang Dihasilkan:
Harga awal : 200000
Diskon 25%: 50000
Harga akhir: 150000Allman Style #
Gaya Allman (juga disebut BSD style) menempatkan kurung kurawal pembuka di baris baru. Keuntungannya: brace pembuka dan penutup berada di kolom yang sama, sehingga mudah melihat pasangannya. Kerugiannya: setiap blok memakai satu baris ekstra.
/* File: format2.c - Allman style (brace on new line) */
#include <stdio.h>
static int hitung_diskon(int harga, int persen)
{
if (persen < 0 || persen > 100)
{
return -1;
}
int potongan = harga * persen / 100;
return harga - potongan;
}
int main(void)
{
int harga = 200000;
int persen = 25;
int akhir = hitung_diskon(harga, persen);
if (akhir < 0)
{
fprintf(stderr, "persen tidak valid\n");
return 1;
}
printf("Harga awal : %d\n", harga);
printf("Diskon %d%%: %d\n", persen, harga - akhir);
printf("Harga akhir: %d\n", akhir);
return 0;
}Output yang Dihasilkan:
Harga awal : 200000
Diskon 25%: 50000
Harga akhir: 150000Linux Kernel Style #
Linux kernel coding style dijabarkan oleh Linus Torvalds di kernel.org/doc/html/latest/process/coding-style.html. Ciri khasnya: indentasi dengan tab (lebar 8 karakter, bukan 4), switch tanpa indentasi pada case, single-statement tanpa brace diperbolehkan untuk kondisi sederhana, dan brace pembuka fungsi di baris baru (sedangkan brace pembuka if/for/while di akhir baris). Tab 8-karakter sengaja dipilih untuk memaksa programmer memecah fungsi yang terlalu dalam (indentation is a hint that you should refactor).
/* File: format3.c - Linux kernel style (tabs, 8-wide, brace same line if/for,
but new line for function. Spaces around binary ops. */
#include <stdio.h>
static int hitung_diskon(int harga, int persen)
{
if (persen < 0 || persen > 100)
return -1;
int potongan = harga * persen / 100;
return harga - potongan;
}
int main(void)
{
int harga = 200000;
int persen = 25;
int akhir = hitung_diskon(harga, persen);
if (akhir < 0) {
fprintf(stderr, "persen tidak valid\n");
return 1;
}
printf("Harga awal : %d\n", harga);
printf("Diskon %d%%: %d\n", persen, harga - akhir);
printf("Harga akhir: %d\n", akhir);
return 0;
}Output yang Dihasilkan:
Harga awal : 200000
Diskon 25%: 50000
Harga akhir: 150000Ketiga versi tersebut menghasilkan output identik—compiler melihat kode yang sama. Yang berbeda adalah cara mata manusia membaca struktur blok.
Indentasi dan Spasi yang Konsisten #
Selain brace style, dua hal yang sering menjadi perdebatan adalah tab vs space dan letak spasi di sekitar operator. Tabel berikut merangkum konvensi yang paling banyak dipakai:
| Aspek | Konvensi umum | Alasan |
|---|---|---|
| Indentasi | 4 space atau 1 tab (lebar 4 atau 8) | Konsistensi |
Spasi sebelum ( fungsi | Tanpa spasi: foo(a) | Membedakan dari keyword if (a) |
Spasi sebelum ( keyword | Dengan spasi: if (a), for (i) | Membedakan dari pemanggilan fungsi |
Spasi sebelum ( pointer deref | Tanpa spasi: (*p) | Mengikuti syntax operator |
| Spasi di sekitar binary op | a + b, c * d | Keterbacaan |
| Spasi di sekitar unary op | Tanpa spasi: -a, *p, i++ | Mengikuti syntax operator |
| Trailing whitespace | Dihapus | Menghindari noise diff |
| Baris terakhir file | Selalu newline | POSIX text file |
Mari kita lihat contoh kode yang mengikuti konvensi di atas:
/* File: format4.c - Demonstrasikan indentasi yang konsisten dengan clang-format */
#include <stdio.h>
typedef enum {
STATUS_OK = 0,
STATUS_INVALID = -1,
STATUS_OVERFLOW = -2
} status_t;
static status_t proses_data(int *out, const int *in, size_t n, int threshold)
{
int total = 0;
for (size_t i = 0; i < n; i++) {
if (in[i] < 0) {
return STATUS_INVALID;
}
if (in[i] > threshold) {
total += threshold;
} else {
total += in[i];
}
}
*out = total;
return STATUS_OK;
}
int main(void)
{
int data[] = {5, 10, 3, 8, 12};
int hasil;
status_t s = proses_data(&hasil, data, 5, 8);
printf("status=%d hasil=%d\n", s, hasil);
return 0;
}Output yang Dihasilkan:
status=0 hasil=32Mari kita telaah baris-baris kode di atas:
- Baris 5-9 (
typedef enum { ... } status_t): EnumeratorSTATUS_OK,STATUS_INVALID,STATUS_OVERFLOWditulis dalam ALL_CAPS untuk menandakan konstanta compile-time. Brace pembuka enum berada di akhir baristypedef enum(gaya K&R). - Baris 11 (
static status_t proses_data(int *out, const int *in, size_t n, int threshold)): Tanda*menempel ke nama variabel (int *out), bukan ke tipe (int* out). Konvensi C mengikuti posisi*ke variabel karenaint *out, *in;adalah dua pointer—bukanint* out, in;yang akan menghasilkanint*danint. - Baris 14 (
for (size_t i = 0; i < n; i++) {): Spasi setelah keywordfor, tanpa spasi setelah nama fungsi. Loop body selalu diapit brace meski hanya satu statement—ini aturan CERT C EXP15-C untuk mencegah bug ketika nanti ditambahkan statement. - Baris 19-23 (
if/elseberantai):elseditulis pada baris yang sama dengan}penutupif—ini idiom K&R. Brace pembukaelse {juga di akhir baris yang sama. - Baris 25 (
*out = total;): Tanpa spasi antara*danoutkarena keduanya adalah satu kesatuan operasi dereferensi.
Otomatisasi dengan clang-format #
Manual formatting tidak realistis untuk proyek besar. Standar industri adalah menggunakan clang-format, sebuah tool dari proyek LLVM yang mengubah format kode sesuai aturan yang didefinisikan di file .clang-format. File ini bisa dipakai bersama oleh seluruh tim melalui version control, sehingga setiap commit memiliki format identik.
Konfigurasi umum untuk proyek C modern:
BasedOnStyle: LLVM
IndentWidth: 4
TabWidth: 4
UseTab: Never
ColumnLimit: 100
AllowShortIfStatementsOnASingleLine: Never
BreakBeforeBraces: Attach
AlignConsecutiveAssignments: true
PointerAlignment: RightSetelah konfigurasi disimpan, jalankan clang-format -i *.c *.h untuk memformat semua file secara in-place. Editor modern seperti VS Code, Vim (dengan plugin), dan CLion terintegrasi langsung dengan clang-format dan memformat kode saat men-save.