An array is a data structure that holds multiple elements of the same type, stored in contiguous memory locations. This structure allows direct access to each element using its index.
For example, to store a student's scores in five subjects, instead of creating five separate variables, we can use an array to efficiently manage the data.
The array marks[5]
represents the student's scores across five subjects, with each score accessible using its respective index:
marks[0]
β Score for subject 1marks[1]
β Score for subject 2- and so on...
int a[5]; // Array declaration without initialization
int b[5] = {2, 3, 4, 5, 6}; // Array declaration with initialization
When an array is initialized, it occupies space in the main memory.
Deallocating memory is essential for several reasons, especially in C++ where memory management is manual:
- Memory Leaks: When dynamically allocated memory isn't properly freed, it remains occupied even after it's no longer needed. Over time, this can consume excessive memory, leading to performance issues or crashes.
- Resource Management: Freeing unused memory ensures better performance and allows other parts of the application or system to utilize those resources effectively.
Memory Corruption: Failing to deallocate memory can also lead to undefined behavior if the program inadvertently writes to memory that it thinks is still valid. This can corrupt data, cause crashes, and lead to difficult-to-debug issues.
RAII: This is a common idiom in C++ programming. It ensures that resources are properly released when they are no longer needed. By following RAII, you create robust and exception-safe code. Using destructors to deallocate memory is a key part of this principle.
We need to manually deallocate the memory in C/C++. In Java, Python, Go, C# no need for manual deallocation. In Rust memory is automatically deallocated when the variable goes out of scope
#include <iostream>
void exampleFunction() {
int localVariable = 10; // localVariable is in scope
std::cout << "Inside function: " << localVariable << std::endl;
} // localVariable goes out of scope here
int main() {
exampleFunction();
// localVariable is not accessible here, it's out of scope
return 0;
}
To illustrate the importance of deallocating memory, consider a simple example with a class that dynamically allocates memory.
for reference struct
- Constructor: Allocates memory for
length
andbreadth
dynamically. - Destructor: Deallocates the memory to prevent memory leaks.
- Creating and Deleting Object: In
main
, theRectangle
object is created usingnew
, and manually deleted usingdelete
. This ensures that the destructor is called, which deallocates the memory.
Deallocating memory in C++ is a critical part of managing resources efficiently. It prevents memory leaks, ensures that resources are available for other uses, and maintains the stability and performance of the application. The use of destructors to handle deallocation is a standard practice that aligns with the RAII principle, making your code more robust and maintainable.
Structures (also called struct) are a way to group several related variables into one place. Each variable in the structure is known as a member of the structure. Unlike an array, a structure can contain many different data types (int, float, char, etc.).
- Collection of data members under one name is structure
- Data members can be of similar type or non similar type
- When structures is called in the main( ) program then it will consume space accordingly to the data members types it contains in the memory.
- An example of structure is a program of a rectangle
For reference struct
Structures is used to combine data under one name, thus some example usage of structures is:
- In Complex numbers
- In student details
- In Employee Details
- Bank Details etc
- Defining Shapes etcβ¦
In the example, We get the size of the struct as 12 instead of 9 as integers take 4 bytes each and char takes 1 byte. But instead, the compiler takes 4 bytes as it's easy for the compiler to read but fur char it assigns only 1 byte from the four. This is called
padding
as it takes more bytes other than what is required.
- Pointer is an address variable that is meant for storing an address, not data itself.
- They are used for indirect access to data.
- For a program to use heap memory, pointers are used.
- To access heap memory and resources outside the main memory like the internet, keyboard, monitor, etc pointers is used. Pointers are also used for parameter passing.
Accessing Heap memory through pointer
When a program is assigned the memory its basically divided into three parts
- code section
- stack memory
- heap memory
The program will have access to stack & code section but won't have access to the heap memory. To access the heap memory we use pointer.
- Accessing Heap memory
- Accessing resources
- parameter passing
Obtain from (a pointer) the address of a data item held in another location.
In C, dynamic memory allocation for accessing memory in the heap is typically done using the standard library functions malloc
, calloc
, realloc
, and free
. These functions are declared in the stdlib.h
header file. Here's a brief explanation of each function and how they are used:
The malloc
function allocates a block of memory on the heap of a specified size and returns a pointer to the beginning of the block.
Formula:
ptr = (cast_type *) malloc(size_in_bytes);
Example:
int *p = (int *)malloc(10 * sizeof(int)); // Allocates memory for an array of 10 integers
The calloc
function allocates memory for an array of elements, initializes them to zero, and returns a pointer to the memory.
Formula:
ptr = (cast_type *) calloc(num_elements, size_of_each_element);
int *p = (int *)calloc(10, sizeof(int)); // Allocates and initializes memory for an array of 10 integers
The realloc
function changes the size of the previously allocated memory block.
Formula:
ptr = (cast_type *) realloc(ptr, new_size_in_bytes);
Example:
p = (int *)realloc(p, 20 * sizeof(int)); // Resizes the previously allocated memory to hold 20 integers
The free
function deallocates the previously allocated memory, releasing it back to the heap.
Formula:
free(ptr);
Example:
free(p); // Frees the memory previously allocated to the pointer p
Hereβs a complete example demonstrating the use of these functions:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p;
// Allocate memory for 10 integers
p = (int *)malloc(10 * sizeof(int));
if (p == NULL) {
printf("Memory allocation failed\n");
return 1;
}
// Use the allocated memory
for (int i = 0; i < 10; i++) {
p[i] = i * 10;
}
// Print the allocated memory
for (int i = 0; i < 10; i++) {
printf("%d ", p[i]);
}
printf("\n");
// Reallocate memory for 20 integers
p = (int *)realloc(p, 20 * sizeof(int));
if (p == NULL) {
printf("Memory reallocation failed\n");
return 1;
}
// Use the reallocated memory
for (int i = 10; i < 20; i++) {
p[i] = i * 10;
}
// Print the reallocated memory
for (int i = 0; i < 20; i++) {
printf("%d ", p[i]);
}
printf("\n");
// Free the allocated memory
free(p);
return 0;
}
- Always check if
malloc
,calloc
, orrealloc
returnsNULL
, which indicates that the memory allocation failed. - After using the allocated memory, always free it using the
free
function to avoid memory leaks.
- A reference is a just another name for the same the variable
- Reference are only available in C++
#include <iostream>
int main() {
int a = 10;
int &r = a; // Reference to 'a'
std::cout << a << std::endl; // Output the value of 'a'
r++; // Increment the value of 'a' through the reference 'r'
std::cout << r << std::endl; // Output the incremented value through 'r'
std::cout << a << std::endl; // Output the incremented value of 'a'
int b = 30; // Correcting missing semicolon
r = b; // Assigning 'b' to 'a' through reference 'r'
std::cout << a << std::endl << r << std::endl; // Output the new value of 'a' and 'r', both should be 30
return 0; // Return 0 to indicate successful execution
}
- This is use for parameter passing
- For writing small functions we use reference
- When variable is already existing , then we can use pointer to structure like
#include <stdio.h>
// Define a struct named Rectangle
struct Rectangle {
int length;
int breadth;
};
int main() {
// Declare and initialize a Rectangle struct variable 'r'
struct Rectangle r = {10, 5};
// Declare a pointer to a Rectangle struct and assign it the address of 'r'
struct Rectangle *p = &r;
// Modify the length of 'r' directly
r.length = 15;
printf("%d\n", r.length); // Output the modified length of 'r'
// Modify the length of 'r' using the pointer 'p' with dereferencing
(*p).length = 20;
printf("%d\n", (*p).length); // Output the modified length of 'r' using dereferencing
// Modify the length of 'r' using the pointer 'p' with the arrow operator
p->length = 25;
printf("%d\n", p->length); // Output the modified length of 'r' using the arrow operator
return 0; // Return 0 to indicate successful execution
}
- Dynamically object created in heap and pointer pointing there
#include <stdio.h>
#include <stdlib.h>
struct Rectangle {
int length;
int breadth;
};
int main() {
struct Rectangle *p;
// Allocate memory for a Rectangle structure in C
p = (struct Rectangle *)malloc(sizeof(struct Rectangle));
//Rectangle *p = new Rectangle;
if (p == NULL) {
printf("Memory allocation failed\n");
return 1; // Return with an error code if memory allocation fails
}
// Assign values to the structure members
p->length = 10;
p->breadth = 5;
// Print the values of the structure members
printf("The length: %d & the breadth: %d\n", p->length, p->breadth);
// Free the allocated memory
free(p);
return 0;
}
- Function is a piece of code which performs a specific task
- Grouping instructions is called function
- They are called as modules or procedures
- The main task can be divided into several small task in the form of functions this type of programming is also called as modular or procedural programming
- It is easy for development
- A group of programmers can work on a single project using functioning
- Functions provide reusability of code
- It can be used in other software projects as well
- you can group function into library
Example :
#include<stdio.h>
int add( int a , int b ) /* Actual parameters */
{
int c;
c = a + b;
return c;
}
int main ( )
{
int x,y,z;
x =10;
y = 5;
z = add (x,y); /* Formal parameters */
printf("The sum of two number is: %d",z);
}
Your explanation of the different parameter passing mechanisms in C and C++ is clear. Here's a polished version of your examples, with additional comments for clarity.
In pass by value, the actual parameters will not be modified if any changes are done to the formal parameters.
#include <stdio.h>
// This function attempts to swap the values of x and y
void swap(int x, int y) {
int temp;
temp = x;
x = y;
y = temp;
}
int main() {
int a, b;
a = 10;
b = 20;
// Call the swap function
swap(a, b);
// Print the values of a and b after the swap function call
printf("%d %d\n", a, b); // Output will be 10 20, not 20 10
return 0;
}
In call by address, the addresses of the actual parameters are passed to the formal parameters, which must be pointers. Any changes done inside the function will modify the actual parameters.
#include <stdio.h>
// This function swaps the values of the integers pointed to by x and y
void swap(int *x, int *y) {
int temp;
temp = *x;
*x = *y;
*y = temp;
}
int main() {
int a, b;
a = 10;
b = 20;
// Call the swap function, passing the addresses of a and b
swap(&a, &b);
// Print the values of a and b after the swap function call
printf("%d %d\n", a, b); // Output will be 20 10
return 0;
}
- One function cannot access the value of another function directly, but it can access it indirectly through pointers.
- Thus, call by address is a suitable mechanism for modifying actual parameters.
References are part of C++ programming. It's one of the useful and powerful mechanisms of this language. To make a function call by reference, we just need to add &
in the parameters. These are the references.
#include <iostream>
// This function swaps the values of x and y using references
void swap(int &x, int &y) {
int temp;
temp = x;
x = y;
y = temp;
}
int main() {
int a, b;
a = 10;
b = 20;
// Call the swap function
swap(a, b);
// Print the values of a and b after the swap function call
std::cout << a << " " << b << std::endl; // Output will be 20 10
return 0;
}
- In C++, references provide a powerful mechanism for directly modifying the actual parameters without needing pointers.
- Pass by Value: Copies of the actual parameters are passed. Changes in the function do not affect the originals.
- Call by Address: Addresses of the actual parameters are passed. The function can modify the originals through pointers.
- Call by Reference: References to the actual parameters are passed (C++ only). The function can modify the originals directly.
- The following function is taking parameter as an array,
int A[ ]
the declared array must be empty and its size must be provides as followsint n
. - The reason for array in parameter being empty is because it acts as a pointer.
- Arrays cannot be passed as values , arrays can only be passed by address.
- As its call by address its possible to change the values of the arrays.
#include <stdio.h>
// Function to modify array elements
void modifyArray(int A[], int n) {
int i;
for (i = 0; i < n; i++) {
A[i] = A[i] * 2; // Modify each element of the array
}
}
// Function to print array elements
void printArray(int A[], int n) {
int i;
for (i = 0; i < n; i++) {
printf("%d ", A[i]); // Print each element of the array
}
printf("\n"); // Print newline for better readability
}
int main() {
int A[5] = {1, 2, 3, 4, 5}; // Initialize array
int n = 5; // Size of the array
printf("Original array: ");
printArray(A, n); // Print original array
modifyArray(A, n); // Modify array elements
printf("Modified array: ");
printArray(A, n); // Print modified array
return 0; // Return 0 to indicate successful execution
}
When sending a structure as a parameter to a function, it can be done by value or by address.
- When passing a structure by value, a separate object is created, and all members are copied.
- Modifying the formal parameter does not affect the actual parameter.
#include <stdio.h>
struct Rectangle {
int length;
int breadth;
};
// Function to calculate the area of the rectangle
int area(struct Rectangle r1) {
return r1.length * r1.breadth;
}
int main() {
struct Rectangle r = {10, 5}; // Initialize a Rectangle structure
printf("%d\n", area(r)); // Call area function with the structure passed by value
return 0; // Return 0 to indicate successful execution
}
In the above example, the area
function takes a Rectangle
structure by value. Any changes made to r1
inside the function do not affect the original structure r
in the main
function.
- When passing a structure by address, the function receives a pointer to the structure.
- Modifying the structure via the pointer affects the original structure.
#include <stdio.h>
struct Rectangle {
int length;
int breadth;
};
// Function to modify the dimensions of the rectangle
void modify(struct Rectangle *r1) {
r1->length = 20;
r1->breadth = 10;
}
int main() {
struct Rectangle r = {10, 5}; // Initialize a Rectangle structure
modify(&r); // Call modify function with the structure passed by address
printf("Length: %d, Breadth: %d\n", r.length, r.breadth); // Output modified dimensions
return 0; // Return 0 to indicate successful execution
}
In the above example, the modify
function takes a pointer to a Rectangle
structure. Changes made to the structure via the pointer affect the original structure r
in the main
function.
Key Points
- Call by Value: A separate copy of the structure is created, and changes to the formal parameter do not affect the actual parameter.
- Call by Address: A pointer to the structure is passed, allowing the function to modify the original structure.
- If you want some function to change the actual parameter then it must be done by call by address or call by reference.
- It is possible to send array as a parameter in pass by value only if its inside the structure , if its just array passing then its not possible by pass by value.
In call by reference, a reference to the actual parameter is passed to the function. This means the function works with the original variable, not a copy. Any changes made to the parameter inside the function will affect the actual parameter.
#include <iostream>
using namespace std;
struct Rectangle {
int length;
int breadth;
};
int area(struct Rectangle &r1) {
return r1.length * r1.breadth;
}
int main() {
struct Rectangle r = {10, 5};
cout << area(r) << endl;
return 0;
}
- The only change we need to do for call by reference is adding
&
in the parameter. - Here the new object is not created but the same object is called
r1
also. - Thus new changes in the values will affect the actual parameters.
#include<stdio.h>
struct Rectangle
{
int length;
int breadth;
}; // Intializing the structure
void initialize(struct Rectangle *r,int l,int b)
{
r->length = l;
r->breadth = b;
}
int area( struct Rectangle r)
{
return r.length*r.breadth;
}
void changelength(struct Rectangle *r, int l)
{
r->length = l;
printf("The length has been modified to: %d\n",r->length);
}
int main()
{
struct Rectangle r;
initialize(&r,10,5);
printf("The area of a rectangle: %d\n",area(r));
changelength(&r,20);
return 0;
}
#include <stdio.h>
#include <iostream>
using namespace std;
struct Rectangle {
int length;
int breadth;
void initialize(int l, int b) {
length = l;
breadth = b;
}
int area() {
return length * breadth;
}
int perimeter() {
return 2 * (length + breadth);
}
};
int main() {
Rectangle r = {0, 0};
int l, b;
printf("Enter the length & breadth: ");
cin >> l >> b;
// cin>>r.length>>r.breadth; Can also initialize like this or create a function for initialization
// Initialize the rectangle using the initialize function
r.initialize(l, b);
// Calculate the area and perimeter
int a = r.area();
int peri = r.perimeter();
// Print the area and perimeter
printf("The area of the rectangle is: %d & the perimeter of the rectangle is: %d\n", a, peri);
return 0;
}
To convert the previous OOP-style struct
to a fully encapsulated class
, we need to use the class
keyword instead of struct
. Additionally, we'll declare the member functions and variables as public
or private
to control access. By default, members of a class
are private, so we need to explicitly specify which members are public.
Here is the converted version:
#include <iostream>
using namespace std;
class Rectangle {
private:
int length;
int breadth;
public:
void initialize(int l, int b) {
length = l;
breadth = b;
}
int area() {
return length * breadth;
}
int perimeter() {
return 2 * (length + breadth);
}
};
int main() {
Rectangle r;
int l, b;
cout << "Enter the length & breadth: ";
cin >> l >> b;
// Initialize the rectangle using the initialize function
r.initialize(l, b);
// Calculate the area and perimeter
int a = r.area();
int peri = r.perimeter();
// Print the area and perimeter
cout << "The area of the rectangle is: " << a << " & the perimeter of the rectangle is: " << peri << endl;
return 0;
}
-
Class Definition:
- The
Rectangle
class now includesprivate
members forlength
andbreadth
, ensuring encapsulation. - The
public
keyword is used to make the member functions accessible from outside the class.
- The
-
Initialization:
- The
initialize
function sets thelength
andbreadth
of the rectangle.
- The
-
Area Calculation:
- The
area
function returns the area of the rectangle.
- The
-
Perimeter Calculation:
- The
perimeter
function returns the perimeter of the rectangle.
- The
-
Main Function:
- The
main
function demonstrates creating aRectangle
object, initializing it, and using its member functions to calculate and display the area and perimeter.
- The
This example demonstrates how to use the class
keyword to create a fully encapsulated object in C++, adhering to the principles of object-oriented programming.
- Mutator Functions: Functions that modify the values of private data members. In this example,
setLength
andsetBreadth
are mutator functions. - Accessor Functions: Functions that access the values of private data members without modifying them. In this example,
getLength
andgetBreadth
are accessor functions.
#include <iostream>
using namespace std;
class Rectangle {
private:
int length;
int breadth;
public:
// Default constructor
Rectangle() {
length = breadth = 1;
}
// Parameterized constructor
Rectangle(int l, int b) {
length = l;
breadth = b;
}
// Method to calculate area
int area() {
return length * breadth;
}
// Method to calculate perimeter
int perimeter() {
return 2 * (length + breadth);
}
// Mutator function to set length
void setLength(int l) {
length = l;
}
// Mutator function to set breadth
void setBreadth(int b) {
breadth = b;
}
// Accessor function to get length
int getLength() {
return length;
}
// Accessor function to get breadth
int getBreadth() {
return breadth;
}
// Destructor
~Rectangle() {
cout << "Destructor called" << endl;
}
};
int main() {
// Create a rectangle object using the parameterized constructor
Rectangle r{10, 5};
// Display area and perimeter
cout << "Area: " << r.area() << endl;
cout << "Perimeter: " << r.perimeter() << endl;
// Display current length and breadth
cout << "Length: " << r.getLength() << endl;
cout << "Breadth: " << r.getBreadth() << endl;
// Modify length and breadth using mutator functions
r.setLength(20);
r.setBreadth(30);
// Display modified length and breadth
cout << "Modified Length: " << r.getLength() << endl;
cout << "Modified Breadth: " << r.getBreadth() << endl;
// Display updated area and perimeter
cout << "Updated Area: " << r.area() << endl;
cout << "Updated Perimeter: " << r.perimeter() << endl;
return 0;
}
Key Points
- Default Constructor: Initializes length and breadth to 1.
- Parameterized Constructor: Initializes length and breadth with given values.
- Destructor: Prints a message when the object is destroyed.
- Mutator Functions:
setLength
andsetBreadth
modify the length and breadth of the rectangle. - Accessor Functions:
getLength
andgetBreadth
return the current values of length and breadth without modifying them.
A template is a simple yet powerful tool in C++. The idea is to pass the data type as a parameter so that we donβt need to write the same code for different data types. For example, a software company may need a sort()
function for different data types. Rather than writing and maintaining multiple codes, we can write one sort()
function and pass the data type as a parameter.
C++ adds two keywords to support templates: template
and typename
. The second keyword can always be replaced by the keyword class
.
#include <iostream>
using namespace std;
// One function works for all data types. This would work
// even for user-defined types if the operator '>' is overloaded
template <typename T>
T myMax(T x, T y) {
return (x > y) ? x : y;
}
int main() {
// Call myMax for int
cout << myMax(3, 7) << endl;
// Call myMax for double
cout << myMax(3.0, 7.0) << endl;
// Call myMax for char
cout << myMax('g', 'e') << endl;
return 0;
}