Pointer & Address

Ketika komputer dijalankan, komputer akan menjalankan sebuah program yang memiliki sebuah alamat/address yang biasanya disimpan didalam memori komputer. Pointer sendiri merupakan Pengalihfungsi sebuah variabel dari a ke b ataupun sebaliknya. Pointer mengimplementasikan address sebagai syarat penggunaannya. Jika kita memiliki sebuah variabel, maka variabel tersebut akan tersimpan didalam sebuah memori yang memiliki sebuah alamat unik khusus (yang kita sebut sebagai address), alamat tersebut dapat digunakan pada variabel lain untuk menghasilkan keluaran nilai yang sama. pointer memiliki dua poin penting yang harus dipelajari, yaitu address dan nilai. berikut ini adalah contoh penggunaan pointer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <stdio.h>
#include <iostream>
#include <math.h>
using namespace std;

int g_int_contoh = 10;

int main() {

printf("Nilainya si G = %i\n", g_int_contoh); //Yang dicetak merupakan Nilai
printf("Alamat Memorinya si G = %i\n", &g_int_contoh); //Yang dicetak merupakan alamat memori (address) dari g_int_contoh

cout<<endl;

int *l_int_penampung = &g_int_contoh; //pendeklarasian variabel pointer l_int_penampung untuk menyimpan alamat memori g_int_contoh

printf("Alamat Memori yang dialihfungsikan si L = %i\n", l_int_penampung); //Yang Dicetak Alamat Memori g_int_contoh
printf("Alamat Memorinya Aslinya si L = %i\n", &l_int_penampung); //Yang Dicetak Memori Asli si L
printf("Nilainya si L = %i\n", *l_int_penampung); //Yang Dicetak sudah pasti nilai g_int_contoh

cout<<endl;

int *l_int_penampung2;

l_int_penampung2 = l_int_penampung;

printf("Alamat Memori yang dialihfungsikan si L2 = %i\n", l_int_penampung2); //Yang Dicetak Alamat Memori g_int_contoh
printf("Alamat Memorinya Aslinya si L2 = %i\n", &l_int_penampung2); //Yang Dicetak Memori Asli si L2
printf("Nilainya si L2 = %i\n", *l_int_penampung2); //Yang Dicetak sudah pasti nilai g_int_contoh

return 0;
}

Structure (Struct)

Struct merupakan tipe data object yang berisi banyak data dan memungkinkan memiliki tipe data berbeda. Setiap variabel yang ada didalam struct memiliki alamat/address memori yang berbeda. Strcut digunakan ketika kita memiliki kumpulan data yang memiliki tipe data berbeda misalkan seperti data user/pengguna yang memiliki variabel username (string), password (string), dan id (int). Untuk mendeklarasikan strcut anda dapat menggunakan keyword struct lalu nama structnya, berikut ini contohnya:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <iostream>
#include <math.h>
using namespace std;

struct dataDiri { //Deklarasi Struct bernama dataDiri
string nama; //Membuat Member Struct (sebagai variabel bertipe data string) bernama nama
string asal; //Membuat Member Struct (sebagai variabel bertipe data string) bernama asal
string sekolah; //Membuat Member Struct (sebagai variabel bertipe data string) bernama sekolah
};

struct dataDiri g_struct_saya; //Membuat variabel g_struct_saya untuk menyimpan struct dataDiri

int main() {

cout<<"Structure Data Diri"<<endl;

//Semua member dapat memiliki isi karena setiap member alamat/address memorinya berbeda
g_struct_saya.nama = "Azhar";
g_struct_saya.asal = "Bintaro";
g_struct_saya.sekolah = "Binus";

cout<<"Nama Saya "<<g_struct_saya.nama<<" Asal saya dari "<<g_struct_saya.asal<<", Saya mengajar di "<<g_struct_saya.sekolah<<endl;
}

Union

Union merupakan tipe data object yang digunakan ketika terjadi dua perbedaan opsi, meskipun bisa menyimpan banyak data, tetapi union menyimpannya didalam satu alamat yang sama (setiap member union saling berbagi alamat memori yang sama). Biasanya union digunakan ketika terdapat opsi pemilihan pada program. Perumpamaan paling sederhana ialah pada sebuah game. sebagai player, kita dapat memilih opsi untuk menggunakan senjata (pistol, pisau, dan senapan angin). Player hanya dapat memilih satu diantara ketiganya untuk digunakan, disinilah biasanya union akan digunakan. untuk mendeklarasikan union anda dapat menggunakan keyword union lalu diikuti nama unionnya, berikut ini contohnya:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <iostream>
#include <math.h>
using namespace std;

#define PISAU 1
#define PISTOL 2
#define RIFFLE 3

union damageSenjata { //Deklarasi Union bernama damageSenjata (Total damage setiap senjata)
int pisau; //Membuat Member Union (sebagai variabel bertipe data int) bernama pisau
int pistol; //Membuat Member Union (sebagai variabel bertipe data int) bernama pistol
int riffles; //Membuat Member Union (sebagai variabel bertipe data int) bernama riffles
};

union damageSenjata g_un_damageSenjata; //Membuat variabel g_un_damageSenjata untuk menyimpan union damageSenjata


int main() {

int userActivateWeapon = PISAU;

//Hanya dapat memiliki isi di salah satu member yang digunakan (alasannya karena menggunakan alamat memori yang sama)
if ( userActivateWeapon == PISAU ) {
g_un_damageSenjata.pisau = 5;
} else if ( userActivateWeapon == PISTOL ) {
g_un_damageSenjata.pistol = 10;
} else if ( userActivateWeapon == RIFFLE ) {
g_un_damageSenjata.riffles = 20;
} else {
/* Do Nothing */
}

cout<<"Union Total Damage Setiap Senjata"<<endl;

//Hasilnya semua member akan sama semua mengikuti hasil yang terpilih pada conditional statement sebelumnya
cout<<"Total Damage Pisau: "<<g_un_damageSenjata.pisau<<endl;
cout<<"Total Damage Pistol: "<<g_un_damageSenjata.pistol<<endl;
cout<<"Total Damage Riffles: "<<g_un_damageSenjata.riffles<<endl;
}

Structure Berindex

Structure berindex merupakan ilmu lanjutan dari structure dimana sebuah structure akan memiliki index, biasanya struct berindex digunakan pada sebuah data yang banyak/masif. berikut contoh dalam membuat struct berindex dan melakukan pengisian nilainya:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <math.h>
#include <stdio.h>
using namespace std;

#define max_index 5

typedef struct{
string email;
string username;
string password;
} database;

database g_struct_login[max_index] = {
/* EMAIL | USERNAME | PASSWORD */
{ "[email protected]", "administrator1", "12345" }, //Index 0
{ "[email protected]", "administrator2", "12345" }, //Index 1
{ "[email protected]", "administrator3", "12345" }, //Index 2
{ "[email protected]", "administrator4", "12345" }, //Index 3
{ "[email protected]", "administrator5", "12345" }, //Index 4
};

int main() {

int index = 0; //Masukkan Index bebas

cout<<g_struct_login[index].email<<endl;
cout<<g_struct_login[index].username<<endl;
cout<<g_struct_login[index].password<<endl;

return 0;
}

Extension

Ekstensi .c

File kode sumber dalam bahasa pemrograman C. Berisi implementasi dari fungsi-fungsi dan logika program.
Contoh: main.c mungkin berisi fungsi main() yang merupakan titik awal dari program.

Ekstensi .h

File header dalam bahasa pemrograman C. Berisi deklarasi fungsi, variabel, dan tipe data yang akan digunakan di file .c.
Contoh: stdio.h berisi deklarasi fungsi seperti printf() dan scanf().

Ekstensi .o

File objek yang dihasilkan setelah kompilasi kode sumber, tetapi sebelum proses linking.
Contoh: main.o, utils.o.

Ekstensi .a

File arsip yang berisi kumpulan file objek, biasanya digunakan untuk pustaka statis.
Contoh: libm.a, libc.a.

Ekstensi .so

File pustaka bersama (shared library) yang dapat digunakan oleh beberapa program secara bersamaan.
Contoh: libm.so, libc.so.

Ekstensi .exe (di Windows) atau tanpa ekstensi (di Unix/Linux)

File eksekusi yang dihasilkan setelah proses linking, siap untuk dijalankan.
Contoh: program.exe (Windows), program (Unix/Linux).

Contoh Kode
File greetings.h:

1
2
3
4
5
6
7
8
9
// greetings.h
#ifndef GREETINGS_H
#define GREETINGS_H

// Deklarasi fungsi untuk menyapa
void sayHello();
void sayGoodbye();

#endif // GREETINGS_H

File greetings.c:

1
2
3
4
5
6
7
8
9
10
11
12
// greetings.c
#include <stdio.h>
#include "greetings.h"

// Definisi fungsi untuk menyapa
void sayHello() {
printf("Hello!\n");
}

void sayGoodbye() {
printf("Goodbye!\n");
}

File main.c:

1
2
3
4
5
6
7
8
// main.c
#include "greetings.h"

int main() {
sayHello();
sayGoodbye();
return 0;
}

Penjelasan:
greetings.h: File header ini mendeklarasikan dua fungsi, sayHello dan sayGoodbye.
greetings.c: File kode sumber ini mendefinisikan fungsi sayHello dan sayGoodbye.
main.c: File kode sumber utama yang menggunakan fungsi sayHello dan sayGoodbye dari greetings.c untuk mencetak pesan sapaan.
Dengan contoh ini, kamu bisa melihat bagaimana file header (greetings.h) digunakan untuk mendeklarasikan fungsi-fungsi yang kemudian diimplementasikan di file kode sumber (greetings.c). File utama (main.c) kemudian menggunakan fungsi-fungsi tersebut untuk menjalankan program.

Keyword dalam C

Keyword pada C merupakan kata kunci yang digunakan sebagai perintah dengan maksud tertentu seperti menentukan tipe data ataupun menentukan alur kode, dsb. Pada C sebenarnya terdapat 32 keyword, tetapi penulis tidak akan membahas semuanya, karena pada pertemuan sebelumnya sebagian besar sudah dibahas dan ini adalah pembahasan terkait keyword tambahan sebagai pelengkap.

Static

Keyword static dalam bahasa C memiliki beberapa fungsi penting tergantung pada konteks penggunaannya:

Variabel Lokal dalam Fungsi

Ketika digunakan dalam deklarasi variabel lokal di dalam fungsi, static membuat variabel tersebut mempertahankan nilainya antara pemanggilan fungsi.
Contoh:

1
2
3
4
5
void count() {
static int callCount = 0;
callCount++;
printf("Fungsi dipanggil %d kali\n", callCount);
}

Pada contoh ini, nilai callCount akan terus bertambah setiap kali fungsi count() dipanggil

Variabel Global dan Fungsi:

Ketika digunakan dalam deklarasi variabel global atau fungsi, static membatasi cakupan (scope) variabel atau fungsi tersebut hanya pada file tempat mereka dideklarasikan.
Contoh:

1
2
3
4
5
6
// file1.c
static int count = 0;

static void increment() {
count++;
}

Variabel count dan fungsi increment() hanya dapat diakses di dalam file1.c

Pustaka Statis

static juga digunakan untuk mendeklarasikan fungsi atau variabel dalam pustaka statis, sehingga mereka tidak dapat diakses dari luar pustaka tersebut.
Dengan menggunakan keyword static, kamu dapat mengontrol cakupan dan masa hidup variabel serta fungsi dalam program C, membuat kode lebih terorganisir dan efisien.

Typedef

Typedef merupakan sebuah keyword pada C/C++ untuk mendefinisikan ulang variabel, fungsi, ataupun tipe data, berikut ini contoh penggunaan typedef:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

//Contoh melakukan definisi ulang nama fungsi
typedef fg_int_tambah(a, b) fg_int_fungsiTambahBerparameter(a, b);

//Contoh melakukan definisi ulang pembuatan fungsi palsu yang berubah menjadi variabel berindex
typedef fg_int_listNama(a) g_int_nama[a];

// Contoh Definisi ulang struct yang menjadi tipe data

// Sebelum menggunakan typedef

struct dataDiri {
string nama;
string asal;
string sekolah;
};

struct dataDiri g_struct_saya; //Wajib menggunakan keyword struct untuk menggunakan struct dataDiri

// Sesudah menggunakan typedef

typedef struct {
string nama;
string asal;
string sekolah;
} dataDiri;

dataDiri g_struct_saya; //Tidak perlu menggunakan keyword struct lagi, karena dataDiri sudah didefinisikan ulang untuk mewakili keyword structnya

Extern

Extern merupakan keyword yang digunakan untuk mendeklarasikan variabel atau fungsi yang dideklarasikan di file lain. Extern digunakan ketika kita ingin menggunakan variabel atau fungsi yang dideklarasikan di file lain. Berikut adalah contoh penggunaan extern:

File variables.h

1
2
3
4
5
6
7
8
// variables.h
#ifndef VARIABLES_H
#define VARIABLES_H

// Deklarasi variabel eksternal
extern int sharedVariable;

#endif // VARIABLES_H

File variables.c

1
2
3
4
5
// variables.c
#include "variables.h"

// Definisi variabel eksternal
int sharedVariable = 10;

File main.c

1
2
3
4
5
6
7
8
9
// main.c
#include <stdio.h>
#include "variables.h"

int main() {
// Mengakses dan mencetak nilai variabel eksternal
printf("Nilai sharedVariable: %d\n", sharedVariable);
return 0;
}

Dengan menggunakan keyword extern, kita dapat mendeklarasikan variabel di satu file dan menggunakannya di file lain. Ini sangat berguna untuk berbagi data antar modul dalam program yang lebih besar.

Untuk mengetahui keyword lain dan masing2 kegunaannya kamu bisa cek di dokumentasi bahasa resmi C.

Memory Management

Manajemen memori dalam bahasa C adalah proses mengelola penggunaan memori oleh program melalui berbagai operasi seperti alokasi, realokasi, dan dealokasi memori. Berikut adalah beberapa konsep penting dalam manajemen memori di C:

Alokasi Memori Statis

Memori dialokasikan pada saat kompilasi.
Contoh: Deklarasi variabel lokal dalam fungsi.

1
int x = 10;

Alokasi Memori Dinamis

Memori dialokasikan pada saat runtime menggunakan fungsi dari pustaka standar C.

Fungsi yang sering digunakan:

  • malloc(): Mengalokasikan blok memori.
  • calloc(): Mengalokasikan dan menginisialisasi beberapa blok memori.
  • realloc(): Mengubah ukuran blok memori yang telah dialokasikan sebelumnya.
  • free(): Mengembalikan memori yang dialokasikan ke sistem.

Contoh penggunaan malloc() dan free():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>

int main() {
int *ptr;
ptr = (int *)malloc(5 * sizeof(int)); // Mengalokasikan memori untuk 5 integer

if (ptr == NULL) {
printf("Alokasi memori gagal!\n");
return 1;
}

for (int i = 0; i < 5; i++) {
ptr[i] = i * 10;
printf("ptr[%d] = %d\n", i, ptr[i]);
}

free(ptr); // Mengembalikan memori yang dialokasikan
return 0;
}

Manajemen Memori Dinamis

  • malloc(): Mengalokasikan blok memori dengan ukuran tertentu.
  • calloc(): Mengalokasikan beberapa blok memori dan menginisialisasinya ke nol.
  • realloc(): Mengubah ukuran blok memori yang telah dialokasikan sebelumnya.
  • free(): Mengembalikan memori yang dialokasikan ke sistem.

malloc()

Fungsi malloc() digunakan untuk mengalokasikan blok memori secara dinamis. Fungsi ini mengembalikan pointer ke blok memori yang dialokasikan.

Contoh:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>

int main() {
int *ptr;
ptr = (int *)malloc(5 * sizeof(int)); // Mengalokasikan memori untuk 5 integer

if (ptr == NULL) {
printf("Alokasi memori gagal!\n");
return 1;
}

for (int i = 0; i < 5; i++) {
ptr[i] = i * 10;
printf("ptr[%d] = %d\n", i, ptr[i]);
}

free(ptr); // Mengembalikan memori yang dialokasikan
return 0;
}

calloc()

Fungsi calloc() digunakan untuk mengalokasikan beberapa blok memori dan menginisialisasinya ke nol. Fungsi ini mengembalikan pointer ke blok memori yang dialokasikan.

Contoh:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>

int main() {
int *ptr;
ptr = (int *)calloc(5, sizeof(int)); // Mengalokasikan memori untuk 5 integer dan menginisialisasinya ke nol

if (ptr == NULL) {
printf("Alokasi memori gagal!\n");
return 1;
}

for (int i = 0; i < 5; i++) {
printf("ptr[%d] = %d\n", i, ptr[i]);
}

free(ptr); // Mengembalikan memori yang dialokasikan
return 0;
}

realloc()

Fungsi realloc() digunakan untuk mengubah ukuran blok memori yang telah dialokasikan sebelumnya.

Contoh:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <stdlib.h>

int main() {
int *ptr;
ptr = (int *)malloc(5 * sizeof(int)); // Mengalokasikan memori untuk 5 integer

if (ptr == NULL) {
printf("Alokasi memori gagal!\n");
return 1;
}

// Mengubah ukuran blok memori menjadi 10 integer
ptr = (int *)realloc(ptr, 10 * sizeof(int));

if (ptr == NULL) {
printf("Realokasi memori gagal!\n");
return 1;
}

for (int i = 0; i < 10; i++) {
ptr[i] = i * 10;
printf("ptr[%d] = %d\n", i, ptr[i]);
}

free(ptr); // Mengembalikan memori yang dialokasikan
return 0;
}

free()

Fungsi free() digunakan untuk mengembalikan memori yang telah dialokasikan oleh malloc(), calloc(), atau realloc().

Contoh:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>

int main() {
int *ptr;
ptr = (int *)malloc(5 * sizeof(int)); // Mengalokasikan memori untuk 5 integer

if (ptr == NULL) {
printf("Alokasi memori gagal!\n");
return 1;
}

// Menggunakan memori yang dialokasikan
for (int i = 0; i < 5; i++) {
ptr[i] = i * 10;
}

free(ptr); // Mengembalikan memori yang dialokasikan
return 0;
}

Pentingnya Manajemen Memori

  • Mengoptimalkan kinerja program dengan menggunakan memori secara efisien.
  • Menghindari kebocoran memori (memory leaks) dengan memastikan memori yang tidak lagi digunakan dikembalikan ke sistem.
  • Menghindari kesalahan seperti dereferensi pointer null atau akses memori yang tidak valid.

Manajemen memori yang baik sangat penting untuk memastikan program berjalan dengan efisien dan bebas dari kesalahan yang terkait dengan penggunaan memori

Bit Field

Bitfield dalam bahasa C adalah cara untuk mengelola memori secara efisien dengan memungkinkan kita mendefinisikan jumlah bit yang tepat untuk merepresentasikan variabel. Bitfield sering digunakan dalam sistem embedded dan pemrograman tingkat perangkat keras di mana penggunaan memori yang efisien sangat penting.

Penggunaan Bitfield

Bitfield memungkinkan kita untuk menentukan ukuran dalam satuan bit dari anggota struct. Ini sangat berguna ketika kita tahu bahwa nilai dari suatu field tidak akan melebihi batas tertentu, sehingga kita dapat menghemat ruang memori.

Contoh Penggunaan Bitfield

Tanpa Bitfield:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

struct date {
unsigned int day;
unsigned int month;
unsigned int year;
};

int main() {
struct date dt = {31, 12, 2024};
printf("Size of date: %lu bytes\n", sizeof(struct date));
printf("Date: %d/%d/%d\n", dt.day, dt.month, dt.year);
return 0;
}

Pada contoh diatas, struct date menggunakan 12 bytes memori (4 bytes untuk setiap unsigned int).

Dengan Bitfield:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

struct date {
unsigned int day : 5; // 5 bit cukup untuk nilai 0-31
unsigned int month : 4; // 4 bit cukup untuk nilai 0-15
unsigned int year : 12; // 12 bit cukup untuk nilai 0-4095
};

int main() {
struct date dt = {31, 12, 2024};
printf("Size of date: %lu bytes\n", sizeof(struct date));
printf("Date: %d/%d/%d\n", dt.day, dt.month, dt.year);
return 0;
}

Pada contoh ini, struct date menggunakan hanya 4 bytes memori karena kita telah menentukan ukuran bit yang tepat untuk setiap field12.

Kelebihan dan Kekurangan

Kelebihan

  • Efisiensi Memori: Menghemat ruang memori dengan menggunakan jumlah bit yang tepat.
  • Kontrol yang Lebih Baik: Memberikan kontrol yang lebih baik atas representasi data.

Kekurangan

  • Portabilitas: Implementasi bitfield dapat berbeda antara compiler, sehingga dapat mempengaruhi portabilitas kode.
  • Kompleksitas: Penggunaan bitfield dapat menambah kompleksitas dalam pengelolaan data.

Bitfield sangat berguna dalam situasi di mana penggunaan memori yang efisien sangat penting, seperti dalam pengembangan sistem embedded dan perangkat keras.

Latihan

Buatlah sistem login dengan mengimplementasikan struct berindex dengan index paling sedikit 10, dimana terdapat dua menu pilihan login menggunakan email/username, Jika email/username atau password salah maka, program akan terus berulang dan Jika email/username dan password benar maka akan muncul tampilan login berhasil. Implementasikan pemrograman berorientasi objek dengan membuat fungsi untuk melakukan input serta fungsi untuk melakukan cek validasi email/username dan password. Berikut outputnya:

1
2
3
4
5
6
7
8
9
10
11
12
-----------------------
| Azhar Rizki Zulma |
| Bintaro |
-----------------------
| Basic Login Systems |
-----------------------
Enter Email/Username: test (Diinput User)
Enter Password: gagal (Diinput User)
Email/Username or Password Wrong, Please try again
Enter Email/Username: Moderator (Diinput User)
Enter Password: momod (Diinput User)
Login Successful