Object-Oriented Programming Lecture 2: Pointer Variables and Dynamic Memory Management

Introduction and Course Information

  • Course Title: Объект хандлагат программчлал (Object-Oriented Programming).
  • Lecture Number: Лекц №2 (Lecture №2).
  • Institution: МУИС, МТЭС (National University of Mongolia, School of Applied Sciences and Engineering).
  • Department: Мэдээлэл Компьютерийн Ухааны Тэнхим (Department of Information and Computer Science).
  • Lecturer: Д-р. Э. Авирмэд (Dr. E. Avirmed).
  • Founding Date of Institution (Logo): 1942.

Classification of Variables

Variables in programming are classified based on how they are stored and their access scope. In this lecture, three primary types are discussed:

  • Автомат (auto) хувьсагч (Automatic variables):
    • Scope: Only accessible within the block or part of the program where they are declared (Заралсан хэсэг).
    • Lifetime: They are automatically created in memory when the program enters their scope and are automatically destroyed when the program leaves that scope.
  • Статик (static) хувьсагч (Static variables):
    • Scope: Accessible within the section where they are declared (Заралсан хэсэг).
    • Lifetime: These variables are created in memory as soon as the program starts running and persist until the program finishes execution.
  • Хаяган (pointer) хувьсагч (Pointer variables):
    • Scope: Accessible within the declared section (Заралсан хэсэг).
    • Lifetime: Generally follow the rules of automatic variables. However, if the address they point to has been reserved using memory operators (like dynamic allocation), the memory remains allocated until it is explicitly released, regardless of the pointer variable's internal scope.

Memory Structure and Addresses

  • Storage Location: Every variable is located in a memory cell (санах ойн үүр).
  • The Cell (Үүр): A memory cell is composed of flip-flops that store 8 bits of information (1 byte).
  • Addressing: Each cell has a specific identification number. This unique number assigned to a memory cell is called the Memory Address (санах ойн хаяг).

Pointer Variables and Basic Operations

  • Definition: A variable that stores the memory address of another variable is called a pointer variable (хаяган хувьсагч).
  • Static Declaration Example:
    • inta;int a;: This reserves 4 bytes of memory automatically for an integer variable.
    • intp=&a;int *p = \&a;: The address of variable aa is assigned as the value to the pointer variable pp.
  • Dynamic Allocation Example:
    • intp=newint;int *p = new int;: This reserves 4 bytes of memory from the heap and assigns the starting address of this reserved memory to the pointer variable pp.
    • p=12;*p = 12;: The value 12 is assigned to the reserved memory location by accessing it through the address stored in the pointer.
    • deletep;delete p;: This command releases the reserved dynamic memory.

Pointer Function Example: testFunc

The following code demonstrates passing variables by reference using pointers to modify their values within a function:

  • Code Structure:
    • Function definition: voidtestFunc(inta,intb)(a)++;b=b10;void testFunc(int *a, int *b) { (*a) ++; *b = *b * 10; }
    • Main execution: inta=10,b=20;int a=10, b=20; followed by testFunc(&a,&b);testFunc(\&a, \&b);.
  • Execution Logic:
    • Initial values: a=10,b=20a=10, b=20.
    • Inside testFunctestFunc, the value at the address of aa is incremented, and the value at the address of bb is multiplied by 10.
    • Output after function call: a=11,b=200a=11, b=200.

Limitations of C-style Arrays

The standard array implementation in the C language faces two significant drawbacks due to fixed memory allocation during compile time:

  1. Inefficiency and Overflow: If the fixed size of the array is significantly larger than the number of data entries, memory usage is inefficient. Conversely, if the size is smaller than the required data, a Buffer Overflow (хэт хэтрэлт) occurs.
  2. Compilation Constraints: These weaknesses exist because the size of a C array must be determined at the stage of converting the program into machine language (compilation).
  • Temporary Solution: One can manually change a CAPACITYCAPACITY constant and recompile the program every time the required size changes, but this is impractical.
  • Dynamic Solution: These issues are solved using dynamic memory mechanisms, which involve two main operations: reserving memory when needed and releasing it once used.

Memory Management Operators

C Language Operators
  • Memory is reserved using the malloc()malloc() family of functions.
  • Memory is released using the free()free() function.
  • These require the header files alloc.halloc.h or stdlib.hstdlib.h.
C++ Language Operators
  • While malloc()malloc() and free()free() can still be used in C++, the language introduces the newnew and deletedelete operators.
  • malloc()malloc() requires extra work such as explicit type casting and manually calculating the memory size required for the object, whereas newnew simplifies this.

The new Operator

  • Functionality: It reserves a portion of memory from the available free memory pool and returns the starting address of that section.
  • Failure State: If there is insufficient memory to fulfill the request, it returns a null address or zero (00).
  • Syntax: new type;new \text{ type};
  • Array Allocation: To prepare memory for an array of 50 real numbers (doubles), the command would be: doubledPtr=newdouble[50];double *dPtr = new double[50];.
Anonymous Variables via new
  • If a pointer variable (e.g., intPtrintPtr) is not null, the newly prepared memory serves as an anonymous variable (нэргүй хувьсагч) because the memory itself does not have a unique identifier name like a standard variable.
  • The return address (e.g., 0x0200x020) becomes the value stored in the pointer variable.
  • Operations on Anonymous Variables:
    • cin>>intPtr;cin >> *intPtr;: Storing user input into the dynamic memory.
    • if(intPtr<100)(intPtr)++;if (*intPtr < 100) (*intPtr)++;: Logical comparison and arithmetic operations.
    • intPtr=100;*intPtr = 100;: Assigning a new value.
Dynamic Array Example with Safety Check
  • intarrayPtr=newint[10];int *arrayPtr = new int[10];: Allocates memory for 10 integers.
  • User Input-based Allocation:
    • cout<<"Howmanyentries?";cin>>numEntries;cout << "How many entries ?"; cin >> numEntries;
    • doubledPtr=newdouble[numEntries];double *dPtr = new double[numEntries];
    • assert(dPtr!=0);assert (dPtr != 0);: Uses the assert function to verify that the memory was successfully allocated.
    • for(inti=0;i<numEntres;i++)cin>>dPtr[i];for (int i=0; i<numEntres; i++) cin >> dPtr[i];: Filling the array with values.

The delete Operator

  • Purpose: Once the memory is no longer needed, the deletedelete operator releases it and returns it to the free memory pool.
  • Relationship with new: newnew is a request to take from the pool; deletedelete is a request to return and register the memory back to the pool. They are interdependent and complementary.
  • Syntax:
    • For single variables: deletepointerVariable;delete pointerVariable;
    • For arrays: delete[]arrayPointerVariable;delete [] arrayPointerVariable;
  • Dangling Pointers: After deleteintPtr;delete intPtr; is executed, the value of the pointer intPtrintPtr becomes undefined but still holds the old address. Attempting to use intPtr*intPtr again will result in errors.
  • Safeguard: It is best practice to set the pointer to zero immediately after deleting: deleteintPtr;intPtr=0;delete intPtr; intPtr = 0;.

Memory Leaks (Санах ойн цоорхой)

  • Definition: A memory leak occurs when dynamic memory is allocated but never released, leading to orphaned memory segments that the program can no longer access but still technically owns.
  • Mechanism of Failure:
    • First Iteration: A pointer (e.g., intPtrintPtr) is used to allocate an array of 10 integers.
    • Subsequent Iterations: If new memory is allocated to the same intPtrintPtr without first using deletedelete on the previous address, the first array's memory remains registered to the program but has no pointer directing to it.
    • Result: Repeated iterations will eventually exhaust the available memory until the condition (intPtr!=0)(intPtr != 0) fails and the program terminates.
  • Prevention Example (Do-While Loop):cpp do { int *intPtr = new int[10]; assert (intPtr != 0); // ... program logic ... delete[] intPtr; // Proper release of memory intPtr = 0; // Clearing the pointer (good habit) cout << "\n Do another (Y or n)?"; cin >> answer; } while (answer != '\n'); &nbsp;&nbsp;&nbsp;&nbsp;