Risos AI Risos AI
Ketik untuk mencari Cari di analisis, proyek riset, dan sesi chat Anda.
Notifikasi
Memuat…
U

Modul 1 — Modul 1 — Dari Kode Sumber ke Proses yang Berjalan

  • Membedakan peran prapemroses, kompilator, penaut, pemuat, dan kernel pada satu program C sederhana, termasuk menyebutkan masukan dan keluaran tiap tahap.
  • Menerapkan rangkaian perkakas kompilasi (opsi -E, -S, -c, serta penautan dengan -I, -L, -l) untuk menghasilkan berkas objek dan berkas eksekusi dari sumber berbilah banyak.
  • Menerapkan alat pemeriksa simbol untuk membaca isi berkas objek dan berkas eksekusi, lalu menjelaskan status tiap simbol (terdefinisi atau belum terselesaikan).
  • Menganalisis pesan galat yang belum pernah dilihat untuk menentukan tahap pembangunan mana yang gagal dan simbol atau berkas mana yang bermasalah.
  • Mengevaluasi usulan perbaikan terhadap kegagalan pembangunan dan menolak perbaikan yang menyasar tahap yang keliru.

Bimbingan suara untuk modul ini

Bahas modul ini langsung dengan Yudha Narendra, jelaskan bagian yang belum jelas dan tanya-jawab sampai paham.

pengantar · 10 menit

Empat Pesan Galat yang Terasa Sama Padahal Berbeda Asal

Satu sore di depan terminal

Rani baru saja memasang Linux di laptopnya untuk mengikuti kelas ini. Program pertamanya sederhana: menghitung luas lingkaran dari jari-jari yang diketik pengguna. Sepuluh baris, tidak lebih. Ia menyimpannya sebagai luas.c, lalu mengetik satu perintah yang ia salin dari catatan kelas:

gcc luas.c -o luas

Dalam dua jam berikutnya ia bertemu empat pesan yang berbeda.

Pesan pertama:

luas.c:1:10: fatal error: matematika.h: No such file or directory
compilation terminated.

Pesan kedua:

luas.c:9:5: error: expected ';' before 'return'

Pesan ketiga:

/usr/bin/ld: /tmp/ccQk3aA.o: in function `main':
luas.c:(.text+0x3e): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status

Pesan keempat muncul setelah program akhirnya berhasil dibangun:

$ ./luas
Segmentation fault (core dumped)

Bagi Rani, keempatnya terasa sama saja: programku gagal. Maka ia memperlakukan keempatnya dengan cara yang sama — menyalin pesan ke mesin pencari, lalu mencoba saran pertama yang muncul. Untuk pesan kedua ia beruntung; menambahkan titik koma memang menyelesaikannya. Untuk pesan ketiga ia membaca saran "tambahkan #include <math.h>", padahal baris itu sudah ada di berkasnya sejak awal. Ia menambahkannya sekali lagi. Tentu saja tidak ada yang berubah.

Yang belum diketahui Rani: keempat pesan itu dikirim oleh program yang berbeda, pada waktu yang berbeda, dan masing-masing hanya melihat sepotong kecil dari keseluruhan pekerjaan.

Satu perintah, lima pekerjaan

Perintah gcc luas.c -o luas terlihat seperti satu tindakan tunggal. Kenyataannya perintah itu memicu serangkaian pekerjaan yang dikerjakan bergantian:

  1. Ada bagian yang hanya mengurus teks: menyisipkan isi berkas lain, mengganti nama makro, membuang blok yang tidak terpakai. Bagian ini tidak paham apa itu fungsi.
  2. Ada bagian yang menerjemahkan kode C menjadi instruksi mesin, tetapi hanya untuk satu berkas sumber pada satu waktu. Ia tidak tahu berkas lain di proyekmu ada.
  3. Ada bagian yang baru melihat semua potongan sekaligus dan bertugas menyambungkannya, termasuk menyambungkan ke pustaka sistem.
  4. Ada bagian yang baru bekerja jauh kemudian — saat kamu mengetik ./luas — untuk memasukkan program ke dalam ingatan mesin.
  5. Dan ada kernel, yang mengawasi program selama berjalan dan menghentikannya kalau program menyentuh alamat yang bukan haknya.

Empat pesan galat Rani berasal dari empat titik berbeda dalam rantai itu. Pesan pertama dari pengurus teks. Pesan kedua dari penerjemah. Pesan ketiga dari penyambung. Pesan keempat bahkan bukan galat pembangunan sama sekali — programnya sudah jadi dan sudah berjalan, lalu dihentikan paksa.

Kenapa ini dibahas paling awal

Ada alasan praktis dan alasan jangka panjang.

Alasan praktisnya: kalau kamu salah menebak tahap, kamu akan memperbaiki bagian yang sehat. Menambahkan #include untuk masalah penyambungan sama sia-sianya dengan mengganti oli untuk ban yang bocor. Keduanya pekerjaan bengkel, tetapi bukan pekerjaan yang sama.

Alasan jangka panjangnya: hampir semua topik lanjutan dalam pemrograman sistem berdiri di atas peta ini. Kenapa satu pustaka bisa dipakai banyak program tanpa disalin berulang. Kenapa sebuah program yang jalan di laptopmu menolak jalan di server. Kenapa mengubah satu berkas header memaksa proyek besar dibangun ulang hampir seluruhnya. Semua pertanyaan itu jawabannya berada di rantai yang sama.

Yang akan kamu kerjakan di modul ini

Kamu tidak akan sekadar menghafal urutan. Kamu akan membongkar satu program kecil menjadi berkas-berkas antara yang biasanya langsung dibuang, membacanya dengan alat pemeriksa simbol, lalu sengaja merusaknya dengan tiga cara berbeda dan mendiagnosis sendiri kerusakannya.

Di akhir modul, target yang wajar: diberi satu pesan galat yang belum pernah kamu lihat, kamu bisa menyebut tahap penyebabnya dalam hitungan detik — sebelum membuka mesin pencari.

Pertanyaan yang kita bawa

Simpan satu pertanyaan ini selama modul berjalan: ketika terminal menolak pekerjaanku, siapa sebenarnya yang sedang berbicara?

Selama kamu belum bisa menjawabnya, setiap perbaikan yang kamu lakukan hanyalah tebakan yang kebetulan kadang berhasil.

Slide 1

Empat pesan, satu perasaan

  • "Programku gagal" — padahal empat penyebab berbeda
  • Header tidak ketemu
  • Titik koma hilang
  • undefined reference
  • Segmentation fault
Tampilkan keempat pesan tanpa penjelasan dulu. Minta peserta menebak mana yang paling berbeda dari yang lain, lalu tahan jawabannya sampai unit kuliah.
Slide 2

Kisah Rani

  • Program sepuluh baris, dua jam macet
  • Menambah include untuk masalah yang bukan include
  • Perbaikan tepat, sasaran salah
Tanyakan siapa yang pernah mengalami hal serupa. Ini membangun rasa aman: kebingungan ini normal, bukan tanda tidak berbakat.
Slide 3

Satu perintah, lima pekerjaan

  • gcc bukan satu program tunggal
  • Teks, terjemahan, penyambungan, pemuatan, pengawasan
  • Masing-masing hanya melihat sepotong cerita
Jangan sebut nama teknis kelima tahap di slide ini. Nama resminya baru diperkenalkan di unit kuliah agar peserta menyimpan gambaran dulu.
Slide 4

Kenapa dibahas paling awal

  • Salah tebak tahap = memperbaiki bagian yang sehat
  • Peta ini dipakai ulang di modul berikutnya
  • Menjelaskan pustaka, portabilitas, dan pembangunan ulang
Sebut satu contoh nyata dari pengalamanmu sendiri: program yang jalan di laptop tapi menolak jalan di server.
Slide 5

Target modul ini

  • Membongkar satu program jadi berkas antara
  • Membaca isinya dengan alat pemeriksa simbol
  • Merusaknya sengaja, lalu mendiagnosis sendiri
Tegaskan bahwa modul ini praktik, bukan hafalan. Pastikan peserta sudah punya terminal Linux siap pakai sebelum unit peragaan.
kuliah · 25 menit

Rantai Penerjemahan: Siapa Tahu Apa, dan Kapan

Gagasan besar unit ini

Mengubah kode sumber menjadi proses yang berjalan bukan satu pekerjaan, melainkan rantai penerjemahan. Setiap mata rantai menerima satu bentuk masukan, mengeluarkan bentuk lain, dan hanya mengetahui sebagian kecil dari keseluruhan cerita. Ketidaktahuan itu bukan kelemahan — justru itulah yang membuat pesan galat bisa dilacak. Kalau kamu tahu apa yang tidak mungkin diketahui sebuah tahap, kamu tahu galat itu tidak mungkin berasal darinya.

1. Prapemroses: hanya melihat teks

Prapemroses menerima berkas .c dan mengeluarkan berkas teks yang lebih panjang. Pekerjaannya tiga hal: menyisipkan isi berkas lain (#include), mengganti makro (#define), dan membuang atau menyimpan blok berdasarkan syarat (#if, #ifdef).

Yang penting dipahami: prapemroses tidak tahu apa itu fungsi, tipe, atau variabel. Baginya int main(void) hanyalah deretan karakter. Karena itu ia hanya bisa gagal karena dua hal: berkas yang diminta tidak ada, atau makro ditulis dengan sintaks yang tidak sah.

Berkas yang dicari #include ditelusuri di daftar direktori tertentu. Opsi -I menambahkan direktori ke daftar itu. Ingat baik-baik: -I adalah urusan prapemroses, dan hanya berpengaruh pada pencarian berkas header.

2. Kompilator: hanya melihat satu berkas

Kompilator menerima teks hasil prapemroses dan menerjemahkannya menjadi instruksi mesin (melalui perakit, yang mengubah teks rakitan menjadi kode biner). Keluarannya adalah berkas objek, biasanya berakhiran .o.

Batasan kompilator inilah sumber kebingungan paling sering di kelas pemula: kompilator bekerja pada satu unit terjemahan pada satu waktu. Satu berkas .c beserta semua header yang disisipkan ke dalamnya membentuk satu unit terjemahan. Kompilator tidak membuka berkas .c lain di proyekmu, dan tidak membuka satu pun pustaka.

Maka muncul pembedaan yang menjadi tulang punggung seluruh modul ini:

  • Deklarasi memberi tahu kompilator bentuk sesuatu: namanya, tipe kembaliannya, tipe argumennya. Contoh: double biaya_listrik(double kwh);
  • Definisi menyediakan isinya: badan fungsi yang berisi kode sesungguhnya.

Dengan deklarasi saja, kompilator sudah cukup senang. Ia bisa memeriksa apakah pemanggilanmu masuk akal, lalu menuliskan catatan di berkas objek: "di sini ada panggilan ke nama ini, alamatnya belum saya ketahui, tolong isikan nanti". Catatan itu disebut simbol yang belum terselesaikan.

Inilah kenapa header yang benar tidak menjamin program jadi. Header berisi deklarasi. Deklarasi memuaskan kompilator, bukan penaut.

3. Penaut: baru melihat semuanya sekaligus

Penaut menerima sekumpulan berkas objek dan pustaka, lalu mengerjakan tiga hal: mencocokkan setiap simbol yang belum terselesaikan dengan definisinya, menata semua potongan ke dalam satu tata letak alamat, dan menuliskan hasilnya sebagai berkas eksekusi.

Kalau ada satu simbol yang tidak ditemukan definisinya di mana pun, penaut menyerah dengan pesan bergaya undefined reference to .... Kalau ada satu simbol yang punya dua definisi, ia juga menyerah, dengan pesan bergaya multiple definition of ....

Pustaka dicari lewat dua opsi berbeda: -L menambahkan direktori tempat pustaka dicari, dan -l menyebut nama pustakanya. Perhatikan simetrinya: -I untuk header, -L dan -l untuk pustaka. Menukar keduanya adalah kesalahan yang sangat sering terjadi dan sangat mudah dihindari begitu kamu tahu tahapnya berbeda.

Ada dua cara penyambungan. Pada penautan statis, kode dari pustaka disalin masuk ke berkas eksekusi. Pada penautan dinamis — yang menjadi bawaan di kebanyakan distribusi Linux — berkas eksekusi hanya mencatat nama pustaka yang ia butuhkan, dan penyambungan sesungguhnya ditunda sampai program dijalankan. Konsekuensinya besar: program yang berhasil dibangun masih bisa gagal berjalan di mesin lain karena pustaka yang dicatat tidak tersedia di sana.

4. Pemuat: bekerja saat kamu mengetik nama program

Saat kamu mengetik ./program, penaut sudah lama selesai bekerja. Yang terjadi berikutnya: kernel diminta menjalankan berkas itu, memeriksa formatnya, lalu menyiapkan ruang alamat untuk proses baru.

Berkas eksekusi di Linux umumnya berformat ELF. Di dalamnya tercatat, antara lain, alamat titik masuk — instruksi pertama yang akan dijalankan. Untuk program yang ditautkan secara dinamis, di dalamnya juga tercatat nama program penerjemah yang harus dijalankan lebih dulu: penaut dinamis. Program itulah yang mencari pustaka bersama yang dibutuhkan, memuatnya, dan menambal alamat-alamat yang tadi ditunda.

Dua hal yang biasanya mengejutkan pemula:

  • Titik masuk program bukan main. Ada kode awalan dari pustaka runtime C yang berjalan lebih dulu, menyiapkan argumen dan lingkungan, baru kemudian memanggil main.
  • Program bisa gagal sebelum satu baris kodemu dijalankan. Kalau pustaka bersama yang dicatat tidak ditemukan, prosesnya berhenti di tahap pemuatan, dengan pesan bergaya error while loading shared libraries.

5. Kernel: mengawasi selama program hidup

Setelah program berjalan, kernel tetap hadir. Ia yang memberi memori, yang melayani permintaan masuk-keluar, dan yang menghentikan proses ketika proses menyentuh alamat yang bukan haknya. Segmentation fault adalah pesan dari lapisan ini — bukan pesan pembangunan sama sekali. Program yang menghasilkan pesan itu adalah program yang berhasil dibangun dengan sempurna lalu melakukan sesuatu yang terlarang saat hidup.

Peta ringkas untuk membaca pesan galat

Ciri pesan Tahap Yang perlu diperiksa
Menyebut nama berkas header, No such file or directory Prapemroses Nama berkas, letaknya, opsi -I
Menyebut nomor baris dan sintaks C (expected ';') Kompilator Kode sumber di baris itu
Menyebut ld, collect2, undefined reference Penaut Berkas objek dan pustaka pada perintah penautan
error while loading shared libraries Pemuat Ketersediaan pustaka bersama di mesin itu
Segmentation fault, program terhenti saat berjalan Kernel Logika program, terutama penunjuk

Hafalkan bukan tabelnya, melainkan alasannya: setiap tahap hanya bisa mengeluhkan hal yang berada dalam jangkauan pengetahuannya.

Slide 1

Gagasan besar

  • Membangun program = rantai penerjemahan
  • Tiap tahap punya masukan, keluaran, dan kosakata sendiri
  • Ketidaktahuan tiap tahap justru memudahkan pelacakan
Tulis kalimat gagasan besar di papan dan biarkan terlihat sepanjang unit. Semua slide berikutnya adalah penjabarannya.
Slide 2

Prapemroses: hanya teks

  • Menyisipkan header, mengganti makro, membuang blok bersyarat
  • Tidak tahu apa itu fungsi atau tipe
  • Opsi -I mengatur pencarian header
Tekankan bahwa prapemroses tidak bisa mengeluh soal tipe. Ini fondasi diagnosis di unit contoh.
Slide 3

Kompilator: satu berkas saja

  • Satu unit terjemahan pada satu waktu
  • Deklarasi = bentuk; definisi = isi
  • Simbol tak terselesaikan dicatat untuk diisi nanti
Ini slide paling penting di modul. Beri jeda; minta peserta menuliskan ulang beda deklarasi dan definisi dengan kalimat sendiri.
Slide 4

Penaut: melihat semuanya

  • Mencocokkan simbol, menata alamat, menulis berkas eksekusi
  • undefined reference = definisi tidak ditemukan
  • -L untuk direktori pustaka, -l untuk namanya
Tuliskan berdampingan: -I header, -L direktori pustaka, -l nama pustaka. Peserta sering menukar ketiganya.
Slide 5

Statis vs dinamis

  • Statis: kode pustaka disalin ke dalam berkas eksekusi
  • Dinamis: hanya namanya yang dicatat
  • Berhasil dibangun belum tentu berhasil dijalankan
Hubungkan ke pengalaman nyata: aplikasi yang jalan di laptop pengembang tapi gagal di server produksi.
Slide 6

Pemuat: sebelum main

  • Kernel menyiapkan ruang alamat proses
  • Penaut dinamis memuat pustaka bersama
  • Titik masuk program bukan main
Banyak peserta terkejut di poin ketiga. Beri waktu bertanya; ini akan dibuktikan langsung di unit peragaan.
Slide 7

Kernel: selama program hidup

  • Memberi memori, melayani masuk-keluar
  • Menghentikan proses yang menyentuh alamat terlarang
  • Segmentation fault bukan galat pembangunan
Tegaskan sekali lagi: program yang segfault adalah program yang berhasil dibangun sempurna.
Slide 8

Peta membaca galat

  • Nama header hilang → prapemroses
  • Nomor baris dan sintaks → kompilator
  • ld atau collect2 → penaut
  • shared libraries → pemuat
Jangan minta peserta menghafal tabel. Minta mereka menjelaskan alasan tiap baris berdasarkan jangkauan pengetahuan tahapnya.
peragaan · 20 menit

Membongkar Satu Program Menjadi Berkas-Berkas Antara

Membongkar satu program C menjadi berkas-berkas antara — dijalankan sungguhan di peladen Risos, bukan rekaman contoh

Yang akan diperagakan

Biasanya gcc mengerjakan seluruh rantai lalu menghapus berkas antaranya tanpa memberi tahu. Di unit ini kita minta ia berhenti di setiap tahap, lalu kita periksa hasilnya satu per satu.

Ikuti sambil mengetik. Keluaran di layarmu tidak akan persis sama dengan yang tertulis di sini — versi kompilator, distribusi, dan arsitektur prosesor berbeda-beda. Yang perlu kamu amati bukan angka atau alamatnya, melainkan pola perubahannya.

Berkas sumbernya:

#include <stdio.h>

int main(void)
{
    printf("Halo, kelas pemrograman sistem!\n");
    return 0;
}

Simpan sebagai halo.c.

Tahap 1 — berhenti setelah prapemroses

$ gcc -E halo.c -o halo.i
$ wc -l halo.c halo.i

Berkas sumbermu hanya tujuh baris. Berkas halo.i biasanya membengkak menjadi ratusan sampai ribuan baris. Yang masuk ke sana adalah seluruh isi stdio.h beserta semua header yang ikut disisipkan olehnya.

Buka bagian akhir halo.i. Kamu akan menemukan main milikmu, masih dalam bentuk C, tetapi baris #include sudah lenyap — sudah digantikan isinya. Cari juga deklarasi printf di dalamnya. Yang kamu temukan adalah deklarasi, bukan badan fungsinya. Di seluruh berkas sepanjang itu tidak ada satu pun baris kode yang benar-benar mencetak ke layar.

Ini bukti pertama bahwa #include tidak membawa masuk kode fungsi.

Tahap 2 — berhenti setelah kompilator

$ gcc -S halo.c -o halo.s

Berkas halo.s berisi bahasa rakitan: teks yang sudah sangat dekat dengan instruksi mesin, tetapi masih bisa dibaca manusia. Di dalamnya kamu akan melihat kalimat yang kamu cetak tersimpan sebagai data, dan sebuah instruksi panggilan ke fungsi pencetak.

Satu kejutan yang sering muncul: pada banyak versi GCC, panggilan printf dengan format sederhana yang diakhiri baris baru diganti menjadi panggilan puts. Kompilator boleh melakukan penggantian semacam ini selama perilaku programnya setara. Jadi kalau nama yang muncul bukan printf, programmu tidak salah.

Tahap 3 — berhenti setelah berkas objek

$ gcc -c halo.c -o halo.o
$ file halo.o
halo.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

Perhatikan kata relocatable. Berkas ini belum punya tata letak alamat final, dan tidak punya titik masuk. Coba jalankan:

$ ./halo.o
bash: ./halo.o: Permission denied

Berkas objek bukan program. Ia potongan yang menunggu disambung.

Sekarang bagian terpenting di unit ini — membaca tabel simbolnya:

$ nm halo.o
0000000000000000 T main
                 U puts

Baca kolom hurufnya:

  • T berarti simbol itu terdefinisi di bagian kode berkas ini. main ada isinya di sini, dan alamat relatifnya sudah tercatat.
  • U berarti simbol itu belum terselesaikan. Kolom alamatnya kosong, karena memang belum ada yang tahu di mana fungsi itu berada.

Huruf lain yang akan sering kamu jumpai: D untuk data yang punya nilai awal, dan B untuk data yang belum diberi nilai awal.

Satu huruf U inilah seluruh isi janji yang dibuat kompilator kepada penaut.

Tahap 4 — penautan

$ gcc halo.o -o halo
$ file halo
halo: ELF 64-bit LSB pie executable, x86-64, ... dynamically linked,
interpreter /lib64/ld-linux-x86-64.so.2, ...

Dua hal berubah. Kata relocatable menjadi executable. Dan muncul keterangan baru: berkas ini dynamically linked, serta menyebut satu program penerjemah — itulah penaut dinamis yang akan dipanggil lebih dulu saat program dijalankan.

Periksa daftar pustaka yang dicatat:

$ ldd halo
    linux-vdso.so.1 (0x00007ffc...)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f...)
    /lib64/ld-linux-x86-64.so.2 (0x00007f...)

Pustaka C tidak disalin ke dalam programmu. Yang tersimpan hanyalah namanya.

Tahap 5 — membuktikan bahwa program tidak mulai dari main

$ readelf -h halo | grep Entry
  Entry point address:               0x1050
$ nm halo | grep -E ' (T|t) '

Bandingkan alamat titik masuk dengan alamat main pada keluaran nm. Keduanya berbeda. Alamat titik masuk itu menunjuk ke _start, kode awalan dari pustaka runtime C yang dijalankan lebih dulu, menyiapkan lingkungan, lalu memanggil main milikmu.

Tahap 6 — membandingkan dengan penautan statis

$ gcc -static halo.c -o halo_statis
$ ldd halo_statis
    not a dynamic executable
$ ls -l halo halo_statis

Ukuran halo_statis jauh lebih besar, karena kode pustaka yang dibutuhkan kini benar-benar disalin masuk. Sebagai gantinya, program itu tidak lagi bergantung pada pustaka bersama di mesin tujuan.

Yang harus tersisa di kepalamu

Satu berkas sumber berubah bentuk empat kali: teks yang membengkak, teks rakitan, potongan biner yang belum berlamat, lalu program utuh yang mencatat siapa saja yang ia butuhkan. Huruf U berubah menjadi alamat yang nyata hanya di satu tempat: meja penaut.

Slide 1

Meminta gcc berhenti di tiap tahap

  • -E berhenti setelah prapemroses
  • -S berhenti setelah kompilator
  • -c berhenti setelah berkas objek
  • Tanpa opsi: seluruh rantai dijalankan
Peragakan langsung di terminal dengan huruf besar. Jangan tampilkan hasilnya dulu; minta peserta menebak apa yang akan muncul.
Slide 2

Hasil prapemroses

  • Tujuh baris membengkak jadi ratusan sampai ribuan
  • Baris include lenyap, isinya masuk
  • Yang ditemukan cuma deklarasi printf, bukan badannya
Buka berkas .i di layar dan gulir cepat. Efek visualnya lebih kuat daripada penjelasan apa pun.
Slide 3

Berkas objek belum bisa dijalankan

  • file menyebutnya relocatable, bukan executable
  • Tidak punya titik masuk
  • Mencoba menjalankannya: Permission denied
Jalankan ./halo.o di depan peserta. Kegagalan yang terlihat langsung lebih membekas daripada peringatan lisan.
Slide 4

Membaca tabel simbol

  • T = terdefinisi di bagian kode
  • U = belum terselesaikan, alamat kosong
  • D dan B = data, dengan dan tanpa nilai awal
Slide paling penting di unit ini. Minta peserta menyalin arti keempat huruf ke catatan mereka sendiri.
Slide 5

Setelah ditautkan

  • relocatable berubah menjadi executable
  • Muncul nama penaut dinamis sebagai penerjemah
  • ldd menampilkan pustaka yang dicatat, bukan disalin
Tekankan kata dicatat. Ini menyiapkan pemahaman kegagalan pemuatan di mesin lain.
Slide 6

Titik masuk bukan main

  • Alamat titik masuk berbeda dari alamat main
  • Kode awalan runtime berjalan lebih dulu
  • Program bisa gagal sebelum kodemu dijalankan
Tunjukkan kedua alamat berdampingan di layar. Bukti angka lebih meyakinkan daripada klaim.
Slide 7

Statis sebagai pembanding

  • ldd menjawab: not a dynamic executable
  • Ukuran berkas jauh lebih besar
  • Tidak lagi bergantung pustaka mesin tujuan
Cukup sebagai pembanding, jangan masuk ke debat statis versus dinamis. Itu bahan modul lanjutan.
contoh · 25 menit

Kasus Dikerjakan: Kalkulator Tagihan Listrik yang Rusak Tiga Kali

Kasusnya

Sebuah warung kopi ingin program kecil untuk memperkirakan tagihan listrik bulanan dari angka pemakaian di meteran. Perhitungannya sengaja disederhanakan, dan angka tarif di bawah ini angka contoh, bukan tarif resmi.

Proyeknya tiga berkas.

tarif.h

#ifndef TARIF_H
#define TARIF_H

double biaya_listrik(double kwh);

#endif

tarif.c

#include "tarif.h"

double biaya_listrik(double kwh)
{
    double tarif_per_kwh = 1500.0;
    return kwh * tarif_per_kwh;
}

tagihan.c

#include <stdio.h>
#include "tarif.h"

int main(void)
{
    double kwh;

    printf("Pemakaian bulan ini (kWh): ");
    if (scanf("%lf", &kwh) != 1) {
        printf("Masukan tidak sah.\n");
        return 1;
    }

    printf("Perkiraan tagihan: Rp %.2f\n", biaya_listrik(kwh));
    return 0;
}

Langkah 1 — kompilasi terpisah, lalu periksa simbolnya

$ gcc -c tagihan.c
$ gcc -c tarif.c
$ nm tagihan.o
                 U biaya_listrik
0000000000000000 T main
                 U printf
                 U scanf
$ nm tarif.o
0000000000000000 T biaya_listrik

Bacalah dua keluaran itu berdampingan. tagihan.o berjanji: ada panggilan ke biaya_listrik, alamatnya belum diketahui. tarif.o menepati: biaya_listrik ada isinya di sini.

Keduanya dikompilasi tanpa satu pun keluhan, padahal masing-masing baru memegang separuh cerita. Itulah batas pengetahuan kompilator.

Langkah 2 — sengaja lupa menyertakan satu berkas objek

$ gcc tagihan.o -o tagihan
/usr/bin/ld: tagihan.o: in function `main':
tagihan.c:(.text+0x5e): undefined reference to `biaya_listrik'
collect2: error: ld returned 1 exit status

Diagnosisnya, baris demi baris:

  • /usr/bin/ld dan collect2 — pengirimnya penaut. Kompilasi sudah lewat.
  • undefined reference to 'biaya_listrik' — nama simbol yang bermasalah disebut terang-terangan.
  • Perbaikan yang benar berada di perintah penautan, bukan di kode sumber.
$ gcc tagihan.o tarif.o -o tagihan
$ ./tagihan
Pemakaian bulan ini (kWh): 120
Perkiraan tagihan: Rp 180000.00

Langkah 3 — kerusakan pertama: header dipindahkan

Anggap tarif.h dirapikan ke dalam subdirektori include/.

$ gcc -c tagihan.c
tagihan.c:2:10: fatal error: tarif.h: No such file or directory
    2 | #include "tarif.h"
      |          ^~~~~~~~~
compilation terminated.

Cirinya: nama berkas header disebut, dan kata fatal error muncul sebelum satu pun galat sintaks. Prapemroses berhenti sebelum kompilator sempat bekerja.

Perbaikannya di tahap yang sama:

$ gcc -Iinclude -c tagihan.c

Perhatikan: menambah -L atau -l di sini tidak akan menolong sedikit pun. Pustaka bukan urusan prapemroses.

Langkah 4 — kerusakan kedua: salah ketik pada definisi

Misalkan nama fungsi di tarif.c tanpa sengaja tertulis biaya_listik (kurang satu huruf), sementara tarif.h dan tagihan.c tetap benar.

$ gcc -c tarif.c
$ gcc -c tagihan.c
$ gcc tagihan.o tarif.o -o tagihan
/usr/bin/ld: tagihan.o: in function `main':
tagihan.c:(.text+0x5e): undefined reference to `biaya_listrik'
collect2: error: ld returned 1 exit status

Kedua berkas lolos kompilasi. Bagaimana bisa? Saat mengompilasi tarif.c, kompilator melihat sebuah fungsi bernama biaya_listik yang tidak dipanggil siapa pun — itu sah. Saat mengompilasi tagihan.c, ia melihat panggilan ke biaya_listrik yang sudah dideklarasikan di header — itu juga sah. Tidak ada satu pun momen di mana kompilator memegang kedua berkas sekaligus.

Alat yang menyelesaikan tebakan ini dalam sepuluh detik:

$ nm tarif.o
0000000000000000 T biaya_listik

Bandingkan huruf per huruf dengan simbol U di tagihan.o. Selisih satu huruf, dan seluruh penautan gagal.

Aturan kerja yang layak dibiasakan: setiap kali bertemu undefined reference, jalankan nm pada berkas objek yang seharusnya memuat definisinya. Salah satu dari tiga hal akan terlihat — namanya berbeda, hurufnya U juga, atau berkasnya memang tidak ikut ditautkan.

Langkah 5 — kerusakan ketiga: program jadi, lalu mati saat dijalankan

Misalkan & pada scanf terlewat:

    if (scanf("%lf", kwh) != 1) {
$ gcc tagihan.c tarif.c -o tagihan
tagihan.c: In function 'main':
tagihan.c:9:16: warning: format '%lf' expects argument of type 'double *',
 but argument 2 has type 'double' [-Wformat=]
$ ./tagihan
Pemakaian bulan ini (kWh): 120
Segmentation fault (core dumped)

Perhatikan kata warning, bukan error. Pembangunan berhasil; berkas tagihan benar-benar terbentuk. Yang gagal adalah programnya saat hidup: nilai sampah diperlakukan sebagai alamat, kernel menolak, proses dihentikan.

Pelajaran yang mahal kalau dipelajari belakangan: pembangunan yang berhasil bukan bukti program benar. Biasakan menyalakan -Wall dan membaca peringatan seserius galat.

Ringkasan diagnosis kasus ini

Pesan Pengirim Tempat perbaikan
fatal error: tarif.h: No such file or directory Prapemroses Letak berkas atau opsi -I
undefined reference to 'biaya_listrik' Penaut Daftar berkas objek, atau nama simbol di definisi
warning: format '%lf' expects ... Kompilator Kode sumber, sebelum program dijalankan
Segmentation fault Kernel Logika program saat berjalan

Catatan tentang pustaka matematika

Kalau kelak kamu memakai fungsi matematika seperti pow atau sqrt, kamu mungkin bertemu undefined reference meski math.h sudah disertakan. Pada banyak sistem, kode fungsi itu berada di pustaka terpisah sehingga perlu ditambahkan -lm di ujung perintah penautan; pada sebagian sistem yang lebih baru hal itu tidak lagi diperlukan. Urutannya penting: penaut memproses argumen dari kiri ke kanan, sehingga pustaka sebaiknya disebut setelah berkas objek yang membutuhkannya. Yang tidak berubah adalah cara mendiagnosisnya — pesan itu tetap berasal dari penaut, dan perbaikannya tetap di perintah penautan, bukan di baris #include.

Slide 1

Kasus: tagihan listrik warung kopi

  • Tiga berkas: tarif.h, tarif.c, tagihan.c
  • Angka tarif hanya contoh, bukan tarif resmi
  • Dibangun dulu sampai jalan, baru dirusak
Bagikan ketiga berkas sebelum sesi. Waktu unit ini habis kalau peserta masih mengetik ulang kode.
Slide 2

Janji dan penepatan janji

  • tagihan.o: U biaya_listrik — berjanji
  • tarif.o: T biaya_listrik — menepati
  • Keduanya lolos kompilasi tanpa keluhan
Tampilkan dua keluaran nm berdampingan di satu layar. Perbandingan visualnya yang mengajar, bukan penjelasannya.
Slide 3

Lupa satu berkas objek

  • Pesan menyebut ld dan collect2
  • Nama simbol bermasalah disebut terang-terangan
  • Perbaikan di perintah penautan, bukan kode sumber
Minta peserta menunjuk kata mana di pesan galat yang mengungkap tahapnya, sebelum kamu menjelaskan.
Slide 4

Kerusakan 1: header dipindah

  • fatal error sebelum galat sintaks apa pun
  • Prapemroses berhenti, kompilator belum sempat bekerja
  • Perbaikannya -I, bukan -L
Tanyakan apa yang terjadi kalau peserta menambah -l di sini. Biarkan mereka menyimpulkan sendiri bahwa tidak berubah apa-apa.
Slide 5

Kerusakan 2: salah ketik satu huruf

  • Kedua berkas lolos kompilasi
  • Kompilator tak pernah memegang dua berkas sekaligus
  • nm menyelesaikan tebakan dalam sepuluh detik
Ini inti unit. Tegaskan kebiasaan kerjanya: undefined reference, langsung nm pada berkas yang seharusnya mendefinisikan.
Slide 6

Kerusakan 3: jadi, lalu mati

  • warning, bukan error — berkas tetap terbentuk
  • Segfault datang dari kernel saat berjalan
  • Pembangunan berhasil bukan bukti program benar
Minta peserta menyalakan -Wall sekarang juga di terminal masing-masing dan menjadikannya kebiasaan.
Slide 7

Tabel diagnosis kasus ini

  • Nama header → prapemroses
  • undefined reference → penaut
  • warning → kompilator
  • Segfault → kernel
Tutup dengan meminta satu peserta menceritakan ulang seluruh kasus dalam empat kalimat tanpa melihat slide.
miskonsepsi · 15 menit

Enam Salah Paham yang Membuat Perbaikanmu Menyasar Tahap yang Keliru

Setiap salah paham berikut punya pola yang sama: masuk akal kalau rantai pembangunan dianggap satu pekerjaan tunggal, dan langsung runtuh begitu tahapnya dipisah. Yang berbahaya bukan salah pahamnya, melainkan perbaikan sia-sia yang lahir darinya.

1. "#include menyalin isi fungsinya ke dalam programku"

Kenapa terasa benar. Setelah menulis #include <stdio.h>, printf bisa dipakai. Kesimpulan yang tampak wajar: berarti kode printf sudah masuk.

Koreksi. Yang disalin masuk adalah teks header, dan isi header hampir seluruhnya berupa deklarasi — nama, tipe kembalian, tipe argumen. Kode mesin printf tetap berada di pustaka C dan baru disambungkan oleh penaut atau, pada penautan dinamis, baru dimuat saat program dijalankan.

Cara membuktikannya sendiri. Jalankan gcc -E lalu cari printf di berkas hasilnya. Yang kamu temukan deklarasi, bukan badan fungsi.

Perbaikan sia-sia yang lahir darinya. Menambahkan #include untuk mengatasi undefined reference — persis kesalahan Rani di unit pertama.

2. "Kalau kompilasi berhasil, program sudah jadi"

Kenapa terasa benar. Perintah gcc -c berkas.c selesai tanpa satu pun pesan. Terasa seperti lampu hijau.

Koreksi. Kompilasi yang berhasil hanya berarti satu unit terjemahan sah secara sintaks dan tipe, bila dilihat sendirian. Ia tidak berkata apa pun tentang: apakah semua fungsi yang dipanggil punya definisi di suatu tempat, apakah semua berkas objek akan disertakan saat penautan, dan apakah program akan berperilaku benar saat berjalan.

Alasannya. Kompilator dirancang bekerja per berkas supaya proyek besar bisa dibangun sebagian-sebagian. Harga dari keleluasaan itu adalah: ia tidak berwenang menilai keutuhan proyek.

3. "gcc itu kompilator"

Kenapa terasa benar. Kita mengetik gcc, dan yang keluar berkas eksekusi. Satu perintah, satu program.

Koreksi. gcc adalah pengatur yang memanggil beberapa program berbeda secara berurutan: prapemroses, kompilator sesungguhnya, perakit, lalu penaut. Bukti paling gampang justru ada di pesan galat. Ketika muncul /usr/bin/ld: ... atau collect2: error: ld returned 1 exit status, yang bicara bukan gcc melainkan penaut yang dipanggilnya.

Kenapa ini penting. Begitu kamu terbiasa membaca nama pengirim di dalam pesan galat, separuh pekerjaan diagnosis sudah selesai sebelum kamu membaca kalimat galatnya.

4. "Berkas .o tinggal dijalankan"

Kenapa terasa benar. Isinya sudah kode mesin, bukan teks. Terasa sudah jadi.

Koreksi. Berkas objek bersifat relocatable: alamat-alamatnya belum final, sebagian simbolnya masih berstatus U, dan ia tidak punya titik masuk. Sistem tidak punya cara memulainya. Coba ./berkas.o dan sistem akan menolak.

Analogi yang membantu. Berkas objek itu seperti bab yang sudah selesai ditulis tetapi belum dijilid, belum bernomor halaman, dan belum punya halaman judul. Isinya benar; bentuknya belum bisa dipakai.

5. "undefined reference artinya berkas header tidak ketemu"

Kenapa terasa benar. Kedua masalah sama-sama terasa seperti "ada sesuatu yang hilang", dan keduanya sering muncul saat memakai kode dari luar.

Koreksi. Keduanya berasal dari tahap yang berbeda dan diperbaiki dengan cara yang berbeda:

  • Header tidak ketemu → prapemroses → pesannya menyebut nama berkas dan No such file or directory → diperbaiki dengan membetulkan letak berkas atau menambah -I.
  • undefined reference → penaut → pesannya menyebut nama simbol dan biasanya menyertakan ld atau collect2 → diperbaiki dengan menyertakan berkas objek atau pustaka yang memuat definisinya, lewat -L dan -l.

Penanda cepat. Perhatikan apa yang disebut pesan itu: nama berkas atau nama simbol. Itu saja sudah memisahkan keduanya.

6. "Program dimulai dari main"

Kenapa terasa benar. Semua buku pengantar C berkata begitu, dan untuk sebagian besar keperluan pemrograman sehari-hari itu memang cukup.

Koreksi. main adalah titik masuk kodemu, bukan titik masuk prosesnya. Sebelum main dijalankan, berturut-turut terjadi: kernel menyiapkan ruang alamat proses, penaut dinamis memuat pustaka bersama dan menambal alamat yang ditunda, lalu kode awalan dari pustaka runtime C menyiapkan argumen dan lingkungan.

Konsekuensi praktisnya besar. Sebuah program bisa gagal sebelum satu baris kodemu dijalankan — misalnya ketika pustaka bersama yang dibutuhkan tidak ada di mesin tujuan. Kalau kamu yakin program dimulai dari main, kegagalan semacam itu akan tampak mustahil, dan kamu akan menghabiskan waktu memeriksa kode yang sebenarnya belum sempat berjalan.

Benang merahnya

Keenam salah paham di atas berakar pada satu kebiasaan berpikir: menganggap pembangunan program sebagai satu kotak hitam. Obatnya bukan menghafal enam koreksi ini, melainkan membiasakan satu pertanyaan sebelum memperbaiki apa pun — tahap mana yang sedang berbicara, dan apakah perbaikan yang aku rencanakan berada di tahap yang sama?

Slide 1

Pola yang sama di keenam salah paham

  • Masuk akal kalau pembangunan dianggap kotak hitam
  • Runtuh begitu tahapnya dipisah
  • Bahayanya: perbaikan yang menyasar tahap keliru
Minta peserta menuliskan satu perbaikan sia-sia yang pernah mereka lakukan, lalu simpan untuk dicocokkan di akhir unit.
Slide 2

include menyalin isi fungsi

  • Yang masuk: deklarasi, bukan kode mesin
  • Kode printf tetap di pustaka
  • Buktikan sendiri dengan gcc -E
Kalau ada waktu, jalankan gcc -E lagi dan cari printf di hasilnya. Bukti langsung mengalahkan penjelasan.
Slide 3

Kompilasi berhasil = program jadi

  • Hanya berarti satu unit terjemahan sah sendirian
  • Tidak menjamin definisi ada di suatu tempat
  • Tidak menjamin perilaku benar saat berjalan
Hubungkan ke kerusakan ketiga di unit contoh: berkas terbentuk, program mati saat dijalankan.
Slide 4

gcc itu kompilator

  • gcc adalah pengatur, bukan satu program
  • Memanggil prapemroses, kompilator, perakit, penaut
  • Pesan ld dan collect2 membuktikannya
Kembali tampilkan pesan galat penautan dari unit contoh dan tunjuk nama pengirimnya.
Slide 5

Berkas .o tinggal dijalankan

  • Alamat belum final, sebagian simbol masih U
  • Tidak punya titik masuk
  • Seperti bab yang belum dijilid
Analogi bab yang belum dijilid biasanya langsung nyantol. Pakai itu, jangan ganti dengan istilah teknis.
Slide 6

undefined reference = header hilang

  • Nama berkas disebut → prapemroses → -I
  • Nama simbol disebut → penaut → -L dan -l
  • Penanda cepat: berkas atau simbol?
Latih penanda cepatnya: tampilkan dua pesan acak, minta peserta menjawab berkas atau simbol dalam tiga detik.
Slide 7

Program dimulai dari main

  • main titik masuk kodemu, bukan prosesnya
  • Kernel, penaut dinamis, kode awalan berjalan lebih dulu
  • Program bisa gagal sebelum kodemu berjalan
Ingatkan bukti angka dari unit peragaan: alamat titik masuk berbeda dari alamat main.
Slide 8

Satu pertanyaan sebelum memperbaiki

  • Tahap mana yang sedang berbicara?
  • Apakah perbaikanku berada di tahap yang sama?
  • Kalau tidak sama, jangan diketik
Tutup unit di sini. Minta peserta membandingkan dengan perbaikan sia-sia yang tadi mereka tuliskan.
kuis · 10 menit

Kuis: Empat Situasi Baru, Empat Keputusan

Petunjuk pengerjaan

Empat soal berikut tidak menanyakan definisi dan tidak bisa diselesaikan dengan mengingat contoh dari unit sebelumnya. Semua situasinya baru: nama berkas, nama fungsi, dan konteksnya belum pernah muncul di modul ini. Yang diuji adalah apakah kamu bisa memakai peta rantai pembangunan pada kasus yang belum kamu kenal.

Cara mengerjakan yang disarankan:

  1. Baca situasinya sampai habis sebelum melihat pilihan jawaban. Sebagian besar informasi penting berada di kalimat terakhir soal.
  2. Sebelum memilih, jawab dulu dua pertanyaan ini untuk dirimu sendiri: tahap mana yang mengeluarkan pesan atau gejala ini? dan apa yang sudah dibuktikan berhasil oleh keterangan di soal? Keterangan tentang apa yang sudah berhasil sama pentingnya dengan keterangan tentang apa yang gagal, karena ia menyingkirkan tahap-tahap yang mustahil menjadi penyebab.
  3. Baru kemudian baca keempat pilihan.

Ketentuan:

  • Satu jawaban benar per soal. Tidak ada pengurangan nilai untuk jawaban salah, jadi jangan biarkan ada soal kosong.
  • Waktu yang wajar sekitar sepuluh menit. Kalau satu soal menghabiskan lebih dari tiga menit, tandai dan lanjut dulu.
  • Kerjakan tanpa membuka terminal. Bukan karena terminal terlarang, melainkan karena tujuan kuis ini menguji apakah peta di kepalamu sudah bisa dipakai tanpa alat bantu. Setelah selesai dan pembahasan dibuka, silakan verifikasi ulang setiap soal di terminalmu — itu justru sangat dianjurkan.

Setelah selesai: baca pembahasan seluruh soal, termasuk soal yang kamu jawab benar. Pembahasan menjelaskan kesalahan berpikir yang diwakili setiap pilihan yang salah, dan sebagian dari kesalahan itu mungkin pernah kamu lakukan tanpa sadar.

Ambang yang disarankan: kalau ada dua soal atau lebih yang salah, kembali ke unit kuliah dan unit contoh sebelum melanjutkan ke unit rangkuman. Peta ini dipakai terus di modul-modul berikutnya, jadi tidak ada gunanya melewatinya dengan setengah paham.

Slide 1

Apa yang diuji

  • Empat situasi baru, bukan pengulangan contoh
  • Yang diuji: memakai peta, bukan mengingat kasus
  • Satu jawaban benar per soal
Tegaskan bahwa mencari kemiripan dengan contoh sebelumnya tidak akan menolong. Ini disengaja.
Slide 2

Urutan mengerjakan

  • Baca situasi sampai habis dulu
  • Tentukan tahap penyebabnya sebelum melihat pilihan
  • Baru baca keempat pilihan
Beri contoh singkat urutan ini dengan soal latihan lisan, tanpa memakai soal kuis yang sebenarnya.
Slide 3

Perhatikan yang sudah berhasil

  • Keterangan keberhasilan menyingkirkan tahap yang mustahil
  • Contoh: kompilasi lolos berarti header ditemukan
  • Sama pentingnya dengan keterangan kegagalan
Ini kebiasaan diagnosis yang paling jarang diajarkan. Ulangi sekali lagi setelah pembahasan dibuka.
Slide 4

Aturan main

  • Sekitar sepuluh menit, tanpa membuka terminal
  • Tidak ada pengurangan nilai untuk jawaban salah
  • Soal macet lebih dari tiga menit: tandai, lanjut
Pastikan semua peserta menutup terminal. Setelah pembahasan, minta mereka memverifikasi ulang di mesin masing-masing.
Slide 5

Setelah kuis

  • Baca pembahasan semua soal, termasuk yang benar
  • Dua salah atau lebih: ulangi unit kuliah dan contoh
  • Peta ini dipakai terus di modul berikutnya
Jangan lewati ambang ini demi mengejar jadwal. Peserta yang setengah paham akan tersendat di modul lanjutan.

1. Sinta menggarap program pencatat stok apotek yang terdiri dari lima berkas sumber. Perintah `gcc -c` pada kelima berkas selesai tanpa satu pun pesan. Saat ditautkan, muncul `undefined reference to 'hitung_diskon'`. Sinta sudah memeriksa tiga hal: nama fungsi tertulis identik di header, di berkas pemanggil, dan di berkas yang mendefinisikannya; berkas pemanggil sudah menyertakan header yang benar; dan `nm diskon.o` menampilkan `0000000000000000 T hitung_diskon`. Apa langkah pemeriksaan yang paling masuk akal berikutnya?

2. Bayu membangun perangkat pemantau suhu ruang server. Semua berkasnya berhasil dikompilasi, tetapi penautan gagal dengan `undefined reference to 'tulis_log'`. Setelah ditelusuri, ternyata `tulis_log` bukan buatan timnya sendiri, melainkan berasal dari pustaka pihak ketiga bernama `liblog` yang berkas pustakanya tersimpan di `/opt/pemantau/lib` dan berkas headernya di `/opt/pemantau/include`. Barisan `#include <log.h>` sudah ada di kode Bayu dan kompilasi memang lolos. Perubahan apa yang tepat pada perintah pembangunan?

3. Tim koperasi sekolah membangun aplikasi kasir di laptop pengembang, dan program berjalan normal di sana. Berkas eksekusinya lalu disalin ke komputer meja kasir yang baru dipasang sistem operasinya. Saat dijalankan di sana, layar langsung menampilkan `./kasir: error while loading shared libraries: libkoperasi.so.1: cannot open shared object file: No such file or directory`, dan tidak ada satu pun keluaran dari program. Apa kesimpulan yang paling tepat?

4. Dewi menulis program pengolah data cuaca. Ia mendefinisikan `#define JUMLAH_TITIK 24;` lalu memakainya sebagai `float suhu[JUMLAH_TITIK];`. Kompilasi gagal dengan pesan yang menunjuk ke baris deklarasi larik itu dan mengeluh tentang bentuk ekspresi yang tidak sah, padahal baris tersebut tampak benar-benar wajar saat dibaca di layar. Berkas objek tidak terbentuk. Alat mana yang paling cepat mengungkap penyebab sebenarnya?

rangkuman · 10 menit

Rangkuman dan Daftar Periksa Mandiri

Satu kalimat yang merangkum seluruh modul

Mengubah kode sumber menjadi proses yang berjalan adalah rantai penerjemahan, dan setiap mata rantai hanya bisa mengeluhkan hal yang berada dalam jangkauan pengetahuannya.

Peta yang sudah kamu bangun

Tahap Masukan Keluaran Yang ia ketahui Bentuk galat khasnya
Prapemroses Berkas .c dan header Teks C yang membengkak Hanya teks; tidak tahu fungsi atau tipe Nama berkas header, No such file or directory
Kompilator (dan perakit) Teks hasil prapemroses Berkas objek .o Satu unit terjemahan saja Nomor baris dan keluhan sintaks atau tipe
Penaut Berkas objek dan pustaka Berkas eksekusi Semua potongan sekaligus undefined reference, multiple definition
Pemuat dan penaut dinamis Berkas eksekusi Proses dalam ingatan Isi berkas dan pustaka di mesin ini error while loading shared libraries
Kernel Proses yang hidup Perilaku program Batas ruang alamat dan hak akses Segmentation fault, proses dihentikan

Enam hal yang paling layak diingat

  1. Deklarasi memuaskan kompilator; definisi memuaskan penaut. Hampir semua kebingungan undefined reference runtuh begitu kalimat ini dipahami.
  2. Kompilator hanya melihat satu berkas. Salah ketik pada nama fungsi bisa lolos kompilasi di kedua berkas dan baru ketahuan saat penautan.
  3. Baca nama pengirim di pesan galat. Kemunculan ld atau collect2 sudah menyelesaikan separuh diagnosis sebelum kalimat galatnya dibaca.
  4. Perhatikan yang disebut pesan itu: nama berkas atau nama simbol. Nama berkas menunjuk ke prapemroses, nama simbol menunjuk ke penaut.
  5. -I untuk header, -L dan -l untuk pustaka. Ketiganya bekerja di tahap yang berbeda dan tidak saling menggantikan.
  6. Pembangunan yang berhasil bukan bukti program benar. Peringatan kompilator dan kegagalan saat berjalan adalah dua hal yang sama sekali terpisah dari keberhasilan pembangunan.

Perintah yang sudah masuk perkakasmu

gcc -E berkas.c -o berkas.i    # berhenti setelah prapemroses
gcc -S berkas.c -o berkas.s    # berhenti setelah kompilator
gcc -c berkas.c -o berkas.o    # berhenti setelah berkas objek
gcc a.o b.o -o program         # tautkan menjadi berkas eksekusi
gcc -Wall -c berkas.c          # nyalakan peringatan; biasakan
nm berkas.o                    # baca tabel simbol: T, U, D, B
file program                   # relocatable atau executable?
ldd program                    # pustaka bersama yang dicatat
readelf -h program             # kepala ELF, termasuk titik masuk

Daftar periksa mandiri sebelum lanjut

Jawab jujur. Tanda centang hanya boleh diberikan kalau kamu bisa melakukannya tanpa membuka catatan ini.

Pemahaman peta

  • [ ] Aku bisa menyebutkan masukan dan keluaran kelima tahap secara berurutan.
  • [ ] Aku bisa menjelaskan dengan kalimatku sendiri beda deklarasi dan definisi, beserta tahap mana yang peduli pada masing-masing.
  • [ ] Aku bisa menjelaskan kenapa dua berkas yang sama-sama lolos kompilasi masih bisa gagal ditautkan.
  • [ ] Aku bisa menceritakan apa yang terjadi antara mengetik ./program dan dijalankannya baris pertama main.

Keterampilan perkakas

  • [ ] Dari satu berkas sumber, aku bisa menghasilkan berkas hasil prapemroses, berkas rakitan, berkas objek, dan berkas eksekusi.
  • [ ] Aku bisa membaca keluaran nm dan menjelaskan arti huruf T, U, D, dan B.
  • [ ] Aku bisa mengompilasi proyek dua berkas secara terpisah lalu menautkannya menjadi satu program.
  • [ ] Aku bisa menyebutkan beda -I, -L, dan -l tanpa ragu.

Kemampuan diagnosis

  • [ ] Diberi pesan galat yang belum pernah kulihat, aku bisa menyebut tahap penyebabnya dalam waktu singkat.
  • [ ] Menghadapi undefined reference, langkah pertamaku adalah memeriksa berkas objek yang seharusnya memuat definisinya — bukan menambah #include.
  • [ ] Aku bisa menjelaskan kenapa program yang berjalan mulus di satu mesin bisa menolak jalan di mesin lain.
  • [ ] Aku bisa membedakan kegagalan pembangunan dari kegagalan saat program berjalan hanya dari bentuk pesannya.

Kalau ada yang belum tercentang

  • Bagian pemahaman peta belum lengkap → kembali ke unit kuliah, terutama bagian deklarasi dan definisi.
  • Bagian keterampilan perkakas belum lengkap → ulangi unit peragaan sambil mengetik sendiri, jangan hanya membaca.
  • Bagian kemampuan diagnosis belum lengkap → ulangi unit contoh, lalu rusak sendiri programnya dengan cara yang berbeda dari yang dicontohkan.

Latihan mandiri yang disarankan

Ambil program apa pun milikmu sendiri yang terdiri dari minimal dua berkas sumber. Rusak dengan tiga cara berbeda, satu per tahap: satu kerusakan yang menghentikan prapemroses, satu yang menghentikan penaut, dan satu yang lolos pembangunan tetapi gagal saat dijalankan. Catat pesan galat yang muncul untuk masing-masing. Simpan catatan itu — kamu akan memakainya lagi.

Peta lima tahap ini bukan pengetahuan sekali pakai. Ia dasar yang dipakai berulang di modul-modul berikutnya, dan hampir setiap topik lanjutan dalam pemrograman sistem akan menunjuk kembali ke salah satu kotaknya.

Slide 1

Satu kalimat untuk seluruh modul

  • Membangun program = rantai penerjemahan
  • Tiap tahap hanya mengeluh dalam jangkauan pengetahuannya
  • Dari situ seluruh diagnosis diturunkan
Minta peserta menutup catatan dan mengucapkan kalimat ini dengan kata-kata sendiri sebelum lanjut.
Slide 2

Peta lima tahap

  • Prapemroses: teks masuk, teks keluar
  • Kompilator: satu unit terjemahan
  • Penaut: semua potongan sekaligus
  • Pemuat dan kernel: setelah program dijalankan
Tampilkan tabel lengkap di layar sebagai rujukan, tetapi jangan dibacakan baris per baris.
Slide 3

Enam hal yang paling layak diingat

  • Deklarasi untuk kompilator, definisi untuk penaut
  • Baca nama pengirim di pesan galat
  • Nama berkas atau nama simbol?
  • Berhasil dibangun bukan bukti benar
Kalau waktu mepet, cukup bahas dua poin pertama. Keduanya paling sering menyelamatkan peserta di modul lanjutan.
Slide 4

Perkakas yang kini kamu punya

  • gcc dengan -E, -S, -c, dan -Wall
  • nm untuk membaca tabel simbol
  • file, ldd, readelf untuk memeriksa berkas jadi
Sarankan peserta menempel daftar ini di dekat meja kerja selama beberapa minggu pertama.
Slide 5

Daftar periksa mandiri

  • Tiga kelompok: peta, perkakas, diagnosis
  • Centang hanya tanpa membuka catatan
  • Tiap kelompok dipasangkan dengan unit pengulangannya
Beri waktu tiga menit di kelas untuk mengisi daftar ini. Kalau dibawa pulang, biasanya tidak dikerjakan.
Slide 6

Latihan mandiri

  • Ambil programmu sendiri, minimal dua berkas
  • Rusak tiga cara: prapemroses, penaut, saat berjalan
  • Catat pesan galatnya, simpan catatannya
Tegaskan bahwa merusak program sendiri adalah latihan sah dan aman, bukan tanda ceroboh.