sinau-c/content/arrays_and_pointers.md

9.7 KiB

---LESSON_INFO--- Learning Objectives:

  • Memahami hubungan antara array dan pointer dalam bahasa C
  • Belajar menggunakan pointer untuk mengakses elemen array
  • Mengenal aritmatika pointer dalam konteks array
  • Memahami alokasi dinamis untuk array

Prerequisites:

  • Pemahaman tentang array dan pointer
  • Pemahaman dasar tentang alokasi memori dinamis

---END_LESSON_INFO---

Array dan Pointer dalam C

Dalam tutorial sebelumnya tentang Pointer, Anda belajar bahwa pointer ke tipe data tertentu bisa menyimpan alamat dari variabel mana pun dari tipe data tertentu itu. Sebagai contoh, dalam kode berikut, variabel pointer pc menyimpan alamat dari variabel karakter c.

char c = 'A';
char *pc = &c;

Di sini, c adalah variabel skalar yang hanya bisa menyimpan satu nilai. Namun, Anda sudah familiar dengan array yang bisa menampung beberapa nilai dari tipe data yang sama dalam blok memori yang dialokasikan secara kontinu. Jadi, Anda mungkin bertanya-tanya, bisakah kita memiliki pointer ke array juga? Memang, kita bisa.

Mari kita mulai dengan contoh kode dan lihat outputnya. Kita akan membahas perilakunya kemudian.

char vowels[] = {'A', 'E', 'I', 'O', 'U'};
char *pvowels = vowels;
int i;

// Print the addresses
for (i = 0; i < 5; i++) {
    printf("&vowels[%d]: %p, pvowels + %d: %p, vowels + %d: %p\\n", i, &vowels[i], i, pvowels + i, i, vowels + i);
}

// Print the values
for (i = 0; i < 5; i++) {
    printf("vowels[%d]: %c, *(pvowels + %d): %c, *(vowels + %d): %c\\n", i, vowels[i], i, *(pvowels + i), i, *(vowels + i));
}

Output khas dari kode di atas ditunjukkan di bawah ini.

&vowels[0]: 0x7ffee146da17, pvowels + 0: 0x7ffee146da17, vowels + 0: 0x7ffee146da17
&vowels[1]: 0x7ffee146da18, pvowels + 1: 0x7ffee146da18, vowels + 1: 0x7ffee146da18
&vowels[2]: 0x7ffee146da19, pvowels + 2: 0x7ffee146da19, vowels + 2: 0x7ffee146da19
&vowels[3]: 0x7ffee146da1a, pvowels + 3: 0x7ffee146da1a, vowels + 3: 0x7ffee146da1a
&vowels[4]: 0x7ffee146da1b, pvowels + 4: 0x7ffee146da1b, vowels + 4: 0x7ffee146da1b

vowels[0]: A, *(pvowels + 0): A, *(vowels + 0): A
vowels[1]: E, *(pvowels + 1): E, *(vowels + 1): E
vowels[2]: I, *(pvowels + 2): I, *(vowels + 2): I
vowels[3]: O, *(pvowels + 3): O, *(vowels + 3): O
vowels[4]: U, *(pvowels + 4): U, *(vowels + 4): U

Seperti yang Anda duga, &vowels[i] memberikan lokasi memori dari elemen ke-i dari array vowels. Selain itu, karena ini adalah array karakter, setiap elemen menempati satu byte sehingga alamat memori berurutan dipisahkan oleh satu byte. Kita juga membuat pointer, pvowels, dan menetapkan alamat dari array vowels kepadanya. pvowels + i adalah operasi yang valid; meskipun secara umum, ini mungkin tidak selalu bermakna (dijelaskan lebih lanjut dalam Aritmatika Pointer ). Secara khusus, output yang ditunjukkan di atas menunjukkan bahwa &vowels[i] dan pvowels + i setara. Silakan ubah tipe data dari variabel array dan pointer untuk mengujinya.

Jika Anda melihat dengan cermat pada kode sebelumnya, Anda akan melihat bahwa kita juga menggunakan notasi yang tampaknya mengejutkan: vowels + i. Selain itu, pvowels + i dan vowels + i mengembalikan hal yang sama — alamat dari elemen ke-i dari array vowels. Di sisi lain, *(pvowels + i) dan *(vowels + i) keduanya mengembalikan elemen ke-i dari array vowels. Mengapa demikian? Ini karena nama dari array itu sendiri adalah pointer (konstan) ke elemen pertama dari array. Dengan kata lain, notasi vowels, &vowels[0], dan vowels + 0 semuanya menunjuk ke lokasi yang sama.

Alokasi Memori Dinamis untuk Array

Sekarang kita tahu bahwa kita bisa menjelajahi array menggunakan pointer. Selain itu, kita juga tahu bahwa kita bisa mengalokasikan memori (kontinu) secara dinamis menggunakan blok pointer. Dua aspek ini bisa dikombinasikan untuk mengalokasikan memori secara dinamis untuk array. Ini diilustrasikan dalam kode berikut.

// Alokasikan memori untuk menyimpan lima karakter
int n = 5;
char *pvowels = (char *) malloc(n * sizeof(char));
int i;

pvowels[0] = 'A';
pvowels[1] = 'E';
*(pvowels + 2) = 'I';
pvowels[3] = 'O';
*(pvowels + 4) = 'U';

for (i = 0; i < n; i++) {
    printf("%c ", pvowels[i]);
}
printf("\\n");

free(pvowels);

Dalam kode di atas, kita mengalokasikan lima byte memori kontinu untuk menyimpan lima karakter. Selanjutnya, kita menggunakan notasi array untuk menjelajahi blok memori seolah-olah pvowels adalah array. Namun, ingat bahwa pvowels sebenarnya adalah pointer. Pointer dan array, secara umum, bukan hal yang sama. Jadi kapan ini berguna? Ingat bahwa saat mendeklarasikan array, jumlah elemen yang akan berisi harus diketahui sebelumnya. Karena itu, dalam beberapa skenario mungkin terjadi bahwa ruang yang dialokasikan untuk array kurang dari ruang yang diinginkan atau lebih. Namun, dengan alokasi memori dinamis, seseorang bisa mengalokasikan hanya sebanyak memori yang diperlukan oleh program. Selain itu, memori yang tidak digunakan bisa dibebaskan segera setelah tidak lagi diperlukan dengan memanggil fungsi free(). Di sisi negatifnya, dengan alokasi memori dinamis, seseorang harus secara bertanggung jawab memanggil free() di mana pun relevan. Jika tidak, kebocoran memori akan terjadi.

Kita menyelesaikan tutorial ini dengan melihat alokasi memori dinamis untuk array dua dimensi. Ini bisa digeneralisasi ke n-dimensi dengan cara yang serupa. Tidak seperti array satu dimensi, di mana kita menggunakan pointer, dalam hal ini kita memerlukan pointer ke pointer, seperti yang ditunjukkan di bawah ini.

int nrows = 2;
int ncols = 5;
int i, j;

// Alokasikan memori untuk nrows pointer
char **pvowels = (char **) malloc(nrows * sizeof(char *));

// Untuk setiap baris, alokasikan memori untuk ncols elemen
pvowels[0] = (char *) malloc(ncols * sizeof(char));
pvowels[1] = (char *) malloc(ncols * sizeof(char));

pvowels[0][0] = 'A';
pvowels[0][1] = 'E';
pvowels[0][2] = 'I';
pvowels[0][3] = 'O';
pvowels[0][4] = 'U';
pvowels[1][0] = 'a';
pvowels[1][1] = 'e';
pvowels[1][2] = 'i';
pvowels[1][3] = 'o';
pvowels[1][4] = 'u';

for (i = 0; i < nrows; i++) {
    for(j = 0; j < ncols; j++) {
        printf("%c ", pvowels[i][j]);
    }
    printf("\\n");
}

// Bebaskan baris-baris individu
free(pvowels[0]);
free(pvowels[1]);

// Bebaskan pointer tingkat atas
free(pvowels);

Tabel Hubungan Array dan Pointer dalam C

Konsep Deskripsi Contoh
Array sebagai pointer Nama array adalah pointer ke elemen pertama arr == &arr[0]
Akses elemen Menggunakan notasi array atau pointer arr[i] == *(arr+i)
Aritmatika pointer Menambah/mengurangi alamat pointer ptr+1
Alokasi dinamis Menggunakan malloc untuk array malloc(n * sizeof(type))

---EXERCISE---

Latihan: Alokasi Dinamis untuk Array Dua Dimensi

Tujuh baris pertama dari Segitiga Pascal ditunjukkan di bawah ini. Perhatikan bahwa baris i berisi i elemen. Karena itu, untuk menyimpan angka-angka dari tiga baris pertama, seseorang akan memerlukan 1 + 2 + 3 = 6 slot memori.

1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1

Lengkapi kode kerangka yang diberikan di bawah ini untuk menyimpan angka-angka dari tiga baris pertama Segitiga Pascal dalam "array" dua dimensi menggunakan alokasi memori dinamis. Perhatikan bahwa Anda harus mengalokasikan tepat enam slot memori untuk menyimpan keenam angka tersebut. Tidak boleh mengalokasikan memori tambahan. Di akhir program, bebaskan semua blok memori yang digunakan dalam program ini.

Requirements:

  • Gunakan alokasi memori dinamis untuk array 2D
  • Alokasikan jumlah memori yang tepat
  • Bebaskan semua memori yang dialokasikan
  • Gunakan pointer ganda (**)

Expected Output:

1
11
121

Try writing your solution in the code editor below!

---EXPECTED_OUTPUT--- 1 11 121 ---END_EXPECTED_OUTPUT---

---INITIAL_CODE--- #include <stdio.h> #include <stdlib.h>

int main() {
    int i, j;

    /* TODO: definisikan variabel pointer 2D di sini */

    /* TODO: lengkapi baris berikut untuk mengalokasikan memori untuk menahan tiga baris */
    pnumbers = (int **) malloc();

    /* TODO: alokasikan memori untuk menyimpan elemen-elemen individu dalam satu baris */
    pnumbers[0] = (int *) malloc(1 * sizeof(int));
    pnumbers[0][0] = 1;

    pnumbers[1][0] = 1;
    pnumbers[1][1] = 1;

    pnumbers[2][0] = 1;
    pnumbers[2][1] = 2;
    pnumbers[2][2] = 1;

    for (i = 0; i < 3; i++) {
        for (j = 0; j <= i; j++) {
            printf("%d", pnumbers[i][j]);
        }
        printf("\\n");
    }

    for (i = 0; i < 3; i++) {
        /* TODO: bebaskan memori yang dialokasikan untuk setiap baris */
    }

    /* TODO: bebaskan pointer tingkat atas */

    return 0;
}

---END_INITIAL_CODE---

---SOLUTION_CODE--- #include <stdio.h> #include <stdlib.h>

int main() {
    int i, j;

    /* TODO: definisikan variabel pointer 2D di sini */
    int **pnumbers;

    /* TODO: Lengkapi baris berikut untuk mengalokasikan memori untuk menahan tiga baris */
    pnumbers = (int **) malloc(3 *sizeof(int *));

    /* TODO: Alokasikan memori untuk menyimpan elemen-elemen individu dalam satu baris */
    pnumbers[0] = (int *) malloc(1 * sizeof(int));
    pnumbers[1] = (int *) malloc(2 * sizeof(int));
    pnumbers[2] = (int *) malloc(3 * sizeof(int));

    pnumbers[0][0] = 1;
    pnumbers[1][0] = 1;
    pnumbers[1][1] = 1;
    pnumbers[2][0] = 1;
    pnumbers[2][1] = 2;
    pnumbers[2][2] = 1;

    for (i = 0; i < 3; i++) {
        for (j = 0; j <= i; j++) {
            printf("%d", pnumbers[i][j]);
        }
        printf("\\n");
    }

    for (i = 0; i < 3; i++) {
        free(pnumbers[i]);
    }

    free(pnumbers);

    return 0;
}

---END_SOLUTION_CODE---