Bahasa Cpp: Pointer, Reference dan Dynamic Memory Allocation

Pointer

Pointer itu sangat powerfull akan tetapi sangat complex, dengan penggunaan yang tepat akan meng-improve effisiensi dan performance. jika salah penggunaan akan terjadi memory leaks, buffer overflow, sehingga Java dan C# menghilangkan pointer.

Jika variable menyimpan value, maka pointer menyimpan memory address.

1. Deklarasi Pointer

Tambahkan * :

type *ptr;

atau type* ptr;

atau type * ptr;

Misal:

int * iPtr;     // Declare a pointer variable called iPtr pointing to an int (an int pointer)
                // It contains an address. That address holds an int value.
double * dPtr;  // Declare a double pointer

deklarasi pointer hanya untuk satu variabel:

int *p1, *p2, i;    // p1 and p2 are int pointers. i is an int
int* p1, p2, i;     // p1 is a int pointer, p2 and i are int
int * p1, * p2, i;  // p1 and p2 are int pointers, i is an int

2. Initialisasi

Seperti yang kita ketahui, sebelum variable diinitialisasi, maka data yang terdapat di variable tersebut adalah acak, ini sangat berbahaya, sehingga kita perlu menginisialisasi pointer dengan valid address,  dengan menggunakan operator address-of (&).

int number = 88;   //deklarasi variabel
int * pNumber;   // deklarasi pointer
pNumber = &number;   // pNumber = address-of Number atau berisi alamat dari variable number

3. Inderection / Dereferencing Operator (*)

(*) disebut juga value pointed to by atau nilai yang ditunjukkan oleh
*p1 = 10;          // value pointed to by p1 = 10 atau nilai yang ditunjukkan oleh p1 diberi nilai 10

Contoh:

/* Test pointer declaration and initialization (TestPointerInit.cpp) */
#include <iostream>
using namespace std;
 
int main() {
   int number = 88;    // Declare an int variable and assign an initial value
   int * pNumber;      // Declare a pointer variable pointing to an int (or int pointer)
   pNumber = &number;  // assign the address of the variable number to pointer pNumber
 
   cout << pNumber << endl;  // Print content of pNumber (0x22ccf0)
   cout << &number << endl;  // Print address of number (0x22ccf0)
   cout << *pNumber << endl; // Print value pointed to by pNumber (88)
   cout << number << endl;   // Print value of number (88)
 
   *pNumber = 99;            // Re-assign value pointed to by pNumber
   cout << pNumber << endl;  // Print content of pNumber (0x22ccf0)
   cout << &number << endl;  // Print address of number (0x22ccf0)
   cout << *pNumber << endl; // Print value pointed to by pNumber (99)
   cout << number << endl;   // Print value of number (99)
                             // The value of number changes via pointer
 
   cout << &pNumber << endl; // Print the address of pointer variable pNumber (0x22ccec)
}

4. Pointer dan Arrays

di C/C++, nama array adalah pointer, yang menunjuk kepada element pertama (index 0) dari array.

int numbers[SIZE] = {11, 22, 44, 21, 41};  // An int array

numbers  sama dengan &number[0].

*numbers sama dengan number[0].

*(numbers+i) sama dengan number[i].

/* Pointer and Array (TestPointerArray.cpp) */
#include <iostream>
using namespace std;
 
int main() {
   const int SIZE = 5;
   int numbers[SIZE] = {11, 22, 44, 21, 41};  // An int array
 
   // The array name numbers is an int pointer, pointing at the
   // first item of the array, i.e., numbers = &numbers[0]
   cout << &numbers[0] << endl; // Print address of first element (0x22fef8)
   cout << numbers << endl;     // Same as above (0x22fef8)
   cout << *numbers << endl;         // Same as numbers[0] (11)
   cout << *(numbers + 1) << endl;   // Same as numbers[1] (22)
   cout << *(numbers + 4) << endl;   // Same as numbers[4] (41)
}

5. Pointer Arithmetics

int numbers[] = {11, 22, 33};
int * iPtr = numbers;
cout << iPtr << endl;        // 0x22cd30
cout << iPtr + 1 << endl;    // 0x22cd34 (increase by 4 - sizeof int)
cout << *iPtr << endl;       // 11
cout << *(iPtr + 1) << endl; // 22 
cout << *iPtr + 1 << endl;   // 12

6. Pointer dan Const

Pointer hanya dapat digunakan untuk membaca Const tetapi tidak dapat merubahnya.

int x;
int y = 10;
const int * p = &y;
x = *p;          // ok: reading p
*p = x;          // error: modifying p, which is const-qualified

Contoh pada function parameters:

// pointers as arguments:
#include <iostream>
using namespace std;

void increment_all (int* start, int* stop)
{
  int * current = start;
  while (current != stop) {
    ++(*current);  // increment value pointed
    ++current;     // increment pointer
  }
}

void print_all (const int* start, const int* stop)
{
  const int * current = start;
  while (current != stop) {
    cout << *current << '\n';
    ++current;     // increment pointer
  }
}

int main ()
{
  int numbers[] = {10,20,30};
  increment_all (numbers,numbers+3);
  print_all (numbers,numbers+3);
  return 0;
}

Hasil: 11,21,31

7. Pointer dan C- String

const char * foo = "hello"; 

8. Pointer to Pointer

c is of type char** and a value of 8092

*c is of type char* and a value of 7230

**c is of type char and a value of 'z'

9. Void Pointers

Dengan void, berarti type datanya no type. sehingga dapat point to sembarang data type.

// increaser
#include <iostream>
using namespace std;

void increase (void* data, int psize)
{
  if ( psize == sizeof(char) )
  { char* pchar; 
    pchar=(char*)data; 
    ++(*pchar); }
  else if (psize == sizeof(int) )
  { int* pint; 
    pint=(int*)data; 
    ++(*pint); }
}

int main ()
{
  char a = 'x';
  int b = 1602;
  increase (&a,sizeof(a));
  increase (&b,sizeof(b));
  cout << a << ", " << b << '\n';
  return 0;
}

Hasil: y, 1603

10. Passing Array in/out dari function

Array yang dipassing ke function adalah sebagai pointer dari element pertama

int max(int numbers[], int size);
int max(int *numbers, int size);
int max(int number[50], int size);

semua akan diperlakukan sama oleh compiler menjadi

int max(int*, int);

dan size dari array akan diabaikan oleh compiler [].

Untuk size array harus dipisah menjadi parameter lain, karena c++ tidak mengenal array bound check.

Ada beberapa cara mempassing array ke function:

1. Menggunakan notasi array

/* Passing array in/out function (TestArrayPassing.cpp) */
#include <iostream>
using namespace std;
 
// Function prototypes
int max(const int arr[], int size);
void replaceByMax(int arr[], int size);
void print(const int arr[], int size);
 
int main() {
   const int SIZE = 4;
   int numbers[SIZE] = {11, 22, 33, 22};
   print(numbers, SIZE);
   cout << max(numbers, SIZE) << endl;
   replaceByMax(numbers, SIZE);
   print(numbers, SIZE);
}
 
// Return the maximum value of the given array.
// The array is declared const, and cannot be modified inside the function.
int max(const int arr[], int size) {
   int max = arr[0];
   for (int i = 1; i < size; ++i) {
      if (max < arr[i]) max = arr[i];
   }
   return max;
}
 
// Replace all elements of the given array by its maximum value
// Array is passed by reference. Modify the caller's copy.
void replaceByMax(int arr[], int size) {
   int maxValue = max(arr, size);
   for (int i = 0; i < size; ++i) {
      arr[i] = maxValue;
   }
}
 
// Print the array's content
void print(const int arr[], int size) {
   cout << "{";
   for (int i = 0; i < size; ++i) {
      cout << arr[i];
      if (i < size - 1) cout << ",";
   }
   cout << "}" << endl;
}

2. Mengunakan pointer

/* Passing array in/out function using pointer (TestArrayPassingPointer.cpp) */
#include <iostream>
using namespace std;
 
// Function prototype
int max(const int *arr, int size);
 
int main() {
   const int SIZE = 5;
   int numbers[SIZE] = {10, 20, 90, 76, 22};
   cout << max(numbers, SIZE) << endl;
}
 
// Return the maximum value of the given array
int max(const int *arr, int size) {
   int max = *arr;
   for (int i = 1; i < size; ++i) {
      if (max < *(arr+i)) max = *(arr+i);
   }
   return max;
}

Reference Variables

Reference variable atau disingkat reference adalah alias atau nama alternatif untuk suatu variable.

Fungsi utama dari reference ini adalah sebagai parameter pada function untuk mensupport pass-by-reference. sehingga function bekerja pada original value bukan clone copy.

Reference mirip dengan pointer, dalam banyak hal, reference dapat digunakan sebagai alternatif dari pointer terutama pada parameter function.

2.1  References (or Aliases) (&)

Ingat!!, symbol & jika digunakan di ekspresi/baris program maka dia mendenote address of operator. tetapi jika digunakan pada deklarasi (termasuk function parameters) maka dia berfungsi sebagai reference variable/alias/nama alternatif.

Syntax:

type &newName = existingName;

type& newName = existingName;

type & newName = existingName;

Contoh:

/* Test reference declaration and initialization (TestReferenceDeclaration.cpp) */
#include <iostream>
using namespace std;
 
int main() {
   int number = 88;          // Declare an int variable called number
   int & refNumber = number; // Declare a reference (alias) to the variable number
                             // Both refNumber and number refer to the same value
 
   cout << number << endl;    // Print value of variable number (88)
   cout << refNumber << endl; // Print value of reference (88)
 
   refNumber = 99;            // Re-assign a new value to refNumber
   cout << refNumber << endl;
   cout << number << endl;    // Value of number also changes (99)
 
   number = 55;               // Re-assign a new value to number
   cout << number << endl;
   cout << refNumber << endl; // Value of refNumber also changes (55)
}

Reference bekerja seperti pointer, reference dideklarasikan sebagai alias dari variable.

Apakah perbedaan antara reference dan pointer?

/* References vs. Pointers (TestReferenceVsPointer.cpp) */
#include <iostream>
using namespace std;
 
int main() {
   int number1 = 88, number2 = 22;
 
   // Create a pointer pointing to number1
   int * pNumber1 = &number1;  // Explicit referencing
   *pNumber1 = 99;             // Explicit dereferencing
   cout << *pNumber1 << endl;  // 99
   cout << &number1 << endl;   // 0x22ff18
   cout << pNumber1 << endl;   // 0x22ff18 (content of the pointer variable - same as above)
   cout << &pNumber1 << endl;  // 0x22ff10 (address of the pointer variable)
   pNumber1 = &number2;        // Pointer can be reassigned to store another address
 
   // Create a reference (alias) to number1
   int & refNumber1 = number1;  // Implicit referencing (NOT &number1)
   refNumber1 = 11;             // Implicit dereferencing (NOT *refNumber1)
   cout << refNumber1 << endl;  // 11
   cout << &number1 << endl;    // 0x22ff18
   cout << &refNumber1 << endl; // 0x22ff18
   //refNumber1 = &number2;     // Error! Reference cannot be re-assigned
                                // error: invalid conversion from 'int*' to 'int'
   refNumber1 = number2;        // refNumber1 is still an alias to number1.
                                // Assign value of number2 (22) to refNumber1 (and number1).
   number2++;   
   cout << refNumber1 << endl;  // 22
   cout << number1 << endl;     // 22
   cout << number2 << endl;     // 23
}

Pass-By-Reference ke dalam Function dengan Reference arguments vs pointer arguments

di c/c++, secara default, argument dari function adalah by value (kecuali array menggunakan pointers). dengan pass-by-value, perubahan di dalam function tidak berpengaruh di luar function.

/* Pass-by-value into function (TestPassByValue.cpp) */
#include <iostream>
using namespace std;
 
int square(int);
 
int main() {
   int number = 8;
   cout <<  "In main(): " << &number << endl;  // 0x22ff1c
   cout << number << endl;         // 8
   cout << square(number) << endl; // 64
   cout << number << endl;         // 8 - no change
}
 
int square(int n) {  // non-const
   cout <<  "In square(): " << &n << endl;  // 0x22ff00
   n *= n;           // clone modified inside the function
   return n;
}
Pass-by-Reference with Pointer Arguments

Untuk keperluan merubah data original diluar function, dapat menggunakan method pass-by-reference:

/* Pass-by-reference using pointer (TestPassByPointer.cpp) */
#include <iostream>
using namespace std;
 
void square(int *);
 
int main() {
   int number = 8;
   cout <<  "In main(): " << &number << endl;  // 0x22ff1c
   cout << number << endl;   // 8
   square(&number);          // Explicit referencing to pass an address
   cout << number << endl;   // 64
}
 
void square(int * pNumber) {  // Function takes an int pointer (non-const)
   cout <<  "In square(): " << pNumber << endl;  // 0x22ff1c
   *pNumber *= *pNumber;      // Explicit de-referencing to get the value pointed-to
}
Pass-by-Reference with Reference Arguments
/* Pass-by-reference using reference (TestPassByReference.cpp) */
#include <iostream>
using namespace std;
 
void square(int &);
 
int main() {
   int number = 8;
   cout <<  "In main(): " << &number << endl;  // 0x22ff1c
   cout << number << endl;  // 8
   square(number);          // Implicit referencing (without '&')
   cout << number << endl;  // 64
}
 
void square(int & rNumber) {  // Function takes an int reference (non-const)
   cout <<  "In square(): " << &rNumber << endl;  // 0x22ff1c
   rNumber *= rNumber;        // Implicit de-referencing (without '*')
}
const” Function Reference/Pointer Parameters
/* Test Function const and non-const parameter (FuncationConstParameter.cpp) */
#include <iostream>
using namespace std;
 
int squareConst(const int);
int squareNonConst(int);
int squareConstRef(const int &);
int squareNonConstRef(int &);
 
int main() {
   int number = 8;
   const int constNumber = 9;
   cout << squareConst(number) << endl;
   cout << squareConst(constNumber) << endl;
   cout << squareNonConst(number) << endl;
   cout << squareNonConst(constNumber) << endl;
 
   cout << squareConstRef(number) << endl;
   cout << squareConstRef(constNumber) << endl;
   cout << squareNonConstRef(number) << endl;
   // cout << squareNonConstRef(constNumber) << endl;
       // error: invalid initialization of reference of
       //  type 'int&' from expression of type 'const int'
}
 
int squareConst(const int number) {
   // number *= number;  // error: assignment of read-only parameter
   return number * number;
}
 
int squareNonConst(int number) {  // non-const parameter
   number *= number;
   return number;
}
 
int squareConstRef(const int & number) {  // const reference
   return number * number;
}
 
int squareNonConstRef(int & number) {  // non-const reference
   return number * number;
}

Passing the Function’s Return Value

/* Passing back return value using reference (TestPassByReferenceReturn.cpp) */
#include <iostream>
using namespace std;
 
int & squareRef(int &);
int * squarePtr(int *);
 
int main() {
   int number1 = 8;
   cout <<  "In main() &number1: " << &number1 << endl;  // 0x22ff14
   int & result = squareRef(number1);
   cout <<  "In main() &result: " << &result << endl;  // 0x22ff14
   cout << result << endl;   // 64
   cout << number1 << endl;  // 64
 
   int number2 = 9;
   cout <<  "In main() &number2: " << &number2 << endl;  // 0x22ff10
   int * pResult = squarePtr(&number2);
   cout <<  "In main() pResult: " << pResult << endl;  // 0x22ff10
   cout << *pResult << endl;   // 81
   cout << number2 << endl;    // 81
}
 
int & squareRef(int & rNumber) {
   cout <<  "In squareRef(): " << &rNumber << endl;  // 0x22ff14
   rNumber *= rNumber;
   return rNumber;
}
 
int * squarePtr(int * pNumber) {
   cout <<  "In squarePtr(): " << pNumber << endl;  // 0x22ff10
   *pNumber *= *pNumber;
   return pNumber;
}
You should not pass Function’s local variable as return value by reference
/* Test passing the result (TestPassResultLocal.cpp) */
#include <iostream>
using namespace std;
 
int * squarePtr(int);
int & squareRef(int);
 
int main() {
   int number = 8;
   cout << number << endl;  // 8
   cout << *squarePtr(number) << endl;  // ??
   cout << squareRef(number) << endl;   // ??
}
 
int * squarePtr(int number) {
   int localResult = number * number;
   return &localResult;
      // warning: address of local variable 'localResult' returned
}
 
int & squareRef(int number) {
   int localResult = number * number;
   return localResult;
      // warning: reference of local variable 'localResult' returned
}
Passing Dynamically Allocated Memory as Return Value by Reference
/* Test passing the result (TestPassResultNew.cpp) */
#include <iostream>
using namespace std;
 
int * squarePtr(int);
int & squareRef(int);
 
int main() {
   int number = 8;
   cout << number << endl;  // 8
   cout << *squarePtr(number) << endl;  // 64
   cout << squareRef(number) << endl;   // 64
}
 
int * squarePtr(int number) {
   int * dynamicAllocatedResult = new int(number * number);
   return dynamicAllocatedResult;
}
 
int & squareRef(int number) {
   int * dynamicAllocatedResult = new int(number * number);
   return *dynamicAllocatedResult;
}

 

Kesimpulan:

Pointer dan references adalah sangat kompeks dan difficult, tetapi dapat mengimprove efficiency dari program

Disarankan untuk menghindari pointer di program, karena akan menyebabkan logical bugs, akan tetapi kita harus faham syntax  pass-by-reference dengan pointer dan references, karena banyak library menggunakan metode ini.

pass-by-value : membuat cloning dan passsing di function, hanya mencopy tidak memodifikasi

pass-by-reference: menggunakan pointer sebagai argument

pass-by-reference dengan reference arguments:  menggunakan nama variabel sebagai argument

pass-by-reference dengan pointer arguments: menggunakan &varName (adrress) sebagai argument.


Dynamic Memory Allocation

DMA di C/C++ adalah mengalokasikan memory oleh programmer secara manual, DMA dialokasikan di Heap, sedangkan non-static dan local variable di Stack

Apa fungsi DMA?

Biasanya digunakan untuk Linked List, Tree, etc

Bagaimana Cara mengalokasikannya?

C menggunakan malloc() dan calloc(), dan free(), C++ juga mensupportnya dan memperkenalkan 2 operator baru new dan delete.

Syntax:

pointer-variable = new data-type;

Contoh:

// Pointer initialized with NULL
// Then request memory for the variable
int *p = NULL; 
p = new int;

OR

// Combine declaration of pointer 
// and their assignment
int *p = new int;

Initialize memory

pointer-variable = new data-type(value);
Example:
int *p = new int(25);
float *q = new float(75.25);

Allocate block of memory

pointer-variable = new data-type[size];

Example:
int *p = new int[10]

Normal Array Declaration vs Using new

Normal array dideallocated oleh compiler, sedangkan DMA harus dibuang oleh programmer.

What if enough memory is not available during runtime?

Agar tidak terjadi exception (error), gunakan ‘notrhow’ untuk mengecek adanya memory yang available

int *p = new(nothrow) int;
if (!p)
{
cout << “Memory allocation failed\n”;
}

Delete Operator

Syntax:

// Release memory pointed by pointer-variable
delete pointer-variable;

delete p;
delete q;
// Release block of memory 
// pointed by pointer-variable
delete[] pointer-variable;
Example:
// It will free the entire array
// pointed by p.
delete[] p;

Contoh:

#include <iostream> 
using namespace std; 
  
int main () 
{ 
    // Pointer initialization to null 
    int* p = NULL; 
  
    // Request memory for the variable 
    // using new operator 
    p = new(nothrow) int; 
    if (!p) 
        cout << "allocation of memory failed\n"; 
    else
    { 
        // Store value at allocated address 
        *p = 29; 
        cout << "Value of p: " << *p << endl; 
    } 
  
    // Request block of memory 
    // using new operator 
    float *r = new float(75.25); 
  
    cout << "Value of r: " << *r << endl; 
  
    // Request block of memory of size n 
    int n = 5; 
    int *q = new(nothrow) int[n]; 
  
    if (!q) 
        cout << "allocation of memory failed\n"; 
    else
    { 
        for (int i = 0; i < n; i++) 
            q[i] = i+1; 
  
        cout << "Value store in block of memory: "; 
        for (int i = 0; i < n; i++) 
            cout << q[i] << " "; 
    } 
  
    // freed the allocated memory 
    delete p; 
    delete r; 
  
    // freed the block of allocated memory 
    delete[] q; 
  
    return 0; 
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *