- What Are
Pointers? - Let's Play A Little With
pointers - Why
Pointersare strong types? Void PointerPointerToPointerPointersas function arguments (Call By Reference)PointersAndArrays- Array As Function Arguments
- Character
Arrays&Pointers Pointers& Multi-DimensionalArraysPointers& Dynamic Memory AllocationPointersAs Function Return- Function
Pointers - Function
Pointers& Callbacks - What Is Memory Leak ?
- Reference
int a;
int *p;
p = &a;
a = 5;
printf("%d\n",&a); // 204 (address of a)
printf("%d\n",p); // 204 (address of a stored in pointer p)
printf("%d\n",&p); // 64 (address of pointer p itself)
printf("%d\n",*p); // 5 (value stored in a)
*p = 8;
printf("%d\n",a); // 8 (value stored in a)
&means address of
Both
*p = 8;&printf("%d\n",*p);are calleddereferencingwhich means that I want the value which is stored inside the address that is stored inside pointerp
int a; // integer
int *p; // pointer to integer
char c; // character
char *p1; // pointer to character
double d; // double
double *p1; //pointer to double- Remember the memory occupied by each data type
- int : 4 bytes
- long : 8 bytes
- char : 1 byte
- float : 4 bytes#include <bits/stdc++.h>
using namespace std;
int main()
{
int a = 10;
int *p;
p = &a;
printf("Address of a is : %d\n",&a);
printf("Address of p is : %d\n",p); // same as a
printf("Value at p is : %d\n",*p); // 10
int b = 20;
*p = b;
printf("Value at p is : %d\n",*p); // 20
}int main()
{
int a = 10;
int *p;
p = &a;
printf("Address of p is : %d\n",p); // address : 200
printf("Address of p+1 is : %d\n",p+1); // address : 204
}3. Why Pointers are strong types (must specify the data type of the variable the pointer will refer to) ?
- Why specifying the data type of Pointers while its job is to just store the address of another variable
Let's see,
p+1means that I want to increase the address by1, but since this pointer points to an integer then it will increase by4bytes (1*4bytes) , that's why we have to specify the data type on which the pointer refers to because when the pointer refers to aninteger, it then knows that each time i will usep+1it will increase by4bytes not likechardata type for example, it will only increase by1byte
The second reason for specify the data type of pointers that it is important in
derefercing, let's assume thatint aoccupies address200,201,202,203, then a pointer refering to variableawill only store first address (200), now when I typeprintf("%d\n",*p)this means as we said before go to address200then print the value stored in it, but in this case the compiler should know the data type of that pointer so that if it is anintegerfor example the compiler will know that from address200till203(Look at4bytes starting from address200) are all reserved for that variable so, it is important to know the data type of the pointer to be able to calculate number of reserved bytes for that variable while bothreadingorwriting(Access/Modify) from that pointer
- Let's see the following example, to understand it well
int main()
{
int a = 1025;// 00000000 00000000 00000100 00000001
int *p;
p = &a;
printf("Size of integer %d\n", sizeof(int)); // 4
printf("Address of p : %d , value of p: %d \n",p,*p); // 200 1025
char *p0;
p0 = (char *)p;
printf("Size of character %d\n", sizeof(char)); // 1
printf("Address of p0 : %d , value of p0: %d \n",p0,*p0); // 200 1
}As we can see the value of char pointer
p0is1, but why ?
Let's observe the binary representation of number 125
00000000 00000000 00000100 00000001when we was printing*pit was referring to an integer so that the 4 bytes are all related to that variable, but when we cast the int pointer to char onep0 = (char *)p;the pointerp0will again store the address of the first byte (same asp), but in this time it will only print it while dereferining (access/modify) as it is a char pointer (it takes only 1 byte) so that its corresponding value now is00000001
Note: the output is a number even when I accessed a char pointer beacuse of
printf("%d")that prints only numbers
int main()
{
int a = 1025;
int *p;
p = &a;
printf("Address of a %d\n", &a); //200
printf("Address of p : %d , value of p: %d \n",p+1,*(p+1)); // 204 garbage
char *p0;
p0 = (char *)p; // Typecasting
printf("Size of character %d\n", sizeof(char)); // 1
printf("Address of c : %d , value of c: %d \n",p0+1,*(p0+1)); // 201 4
}The value of
*(p0+1)is4because I have moved1 byteso nowp0refers to00000100byte the second byte in
00000000 00000000 00000100 00000001
printf("Size of int %d\n",sizeof(int));int main()
{
int a = 10;
int *p;
p = &a;
printf("Address of p is : %d\n",p); // address : 200
printf("Address of p+1 is : %d\n",p+1); // address : 204
printf("Value at p+1 is : %d\n",*(p+1)); // garbage value
}int main()
{
int a = 1025;
int *p;
p = &a;
void *p0;
p0 = p; // like (void*) p
printf("Address %d\n",p);
printf("Address %d\n",p0); // we cannot dereference this pointer
//printf("Value = %d\n",*p0); // we cannot dereference this pointer
//printf("Address of next %d\n",p0+1); // we cannot perform any arithmetic operation on this pointer
}
voidpointers have some use cases will be discussed later
- Let's see the following example:
int main()
{
int x = 5; // address 225
int *p = &x;
*p = 6;
int **q = &p;
int ***r = &q;
printf("%d\n",*p); // 6
printf("%d\n",*q); // 225 (address stored in p)
printf("%d\n",*(*q)); // 6
printf("%d\n",*(*r)); //2525
printf("%d\n",*(*(*r))); //6
***r = 10;
printf("%d\n",x); //10
**q = *p + 2; // 10 + 2 = 12
printf("%d\n",x); //12
}- Following code is an example of
Call By Value
#include <bits/stdc++.h>
using namespace std;
void Increment(int a)
{
a++;
}
int main()
{
int a;
a = 10;
Increment(a);
printf("a = %d\n",a); // 10
}This is because
avariable inIncrement()function is different fromavariable inmain(), asavariable inIncrement()function is created in the stack frame of this function while the otheravariable inmain()function is created in the stack frame ofmain()
- To prove that try to print the address of
ain both functions, the address will be different
void Increment(int a)// Called Formal Argument
{
a++;
printf("%d" , &a); // 200
}
int main()
{
int a;
a = 10;
Increment(a); // Called Actual Argument
printf("%d" , &a); // 500
//printf("a = %d\n",a);
}- To
Pass By Reference, just write this :
void Increment(int *a)
{
*a= *a + 1;
}
int main()
{
int a;
a = 10;
Increment(&a);
printf("a = %d\n",a); // 11
}- Observe the following code:
int main()
{
int arr[5] = {1,2,3,4,5}; //Address 200 204 208 212 216
int *p;
p = &arr[0]; // store address of 200
printf("%d\n",&arr[4]); // 216
printf("%d\n",arr); // 200
printf("%d\n",*arr); // 1
printf("%d\n",arr+1); // 204
printf("%d\n",*(arr+1)); // 2
printf("%d\n",p); // 200
printf("%d\n",*p); // 1
printf("%d\n",(p+2)); // 200 + 2*4 = 208
printf("%d\n",*(p+2)); // 3
p++; // Allowed
// arr++; // Error
}Address At index
i-&arr[i]or(arr+i)
Value At indexi-arr[i]or*(arr+i)
Note:p++is allowed, whilearr++is not allowed
- Observe the following code:
#include <bits/stdc++.h>
using namespace std;
void Double(int arr[], int sz) {
int SZ = sizeof(arr)/sizeof(arr[0]); // 2
printf("Size of arr %d & Size of arr[0] %d \n",sizeof(arr),sizeof(arr[0]));// 8 4
for (int i = 0; i < sz; i++) {
*(arr + i) = *(arr + i) * 2;
// Or
// arr[i] = arr[i] * 2;
}
}
int main() {
int arr[5] = {1, 2, 3, 4, 5}; // Address 200 204 208 212 216
int sz = sizeof(arr) / sizeof(arr[0]);
Double(arr, sz);
for (int i = 0; i < sz; i++) {
printf("%d\n", *(arr + i));
}
}- Some Important points from above code :
- When an
arrayis passed as a parameter, it is passed by reference not by value this means that any change inDouble()function toarray's element will affect thearrayinmain(), when you typeint arr[]as a parameter inDouble()function you actually pass the address of first element, soarrayswill be passed in a function as a parameter is treated like pointers this is done to prevent memory wasting if you are passing anarraywith large number of elements, instead of creating anotherarrayhaving the same elements, you just can access the same array int SZ = sizeof(arr)/sizeof(arr[0]); // 2, the reason thatSZ= 2 is thatsizeof(arr)will be8, as this is the size of apointerin modern compilers, so don't ever calculate the size of a passedarrayin a function withsizeof(), always send the size in another parameter like thisDouble(int arr[], int sz)- Referring to point
2, you can passint *arrinstead ofint arr[]& still having the same result, but this time you are using apointerthat refers to the first byte of first element in the array, but in this case you will pass in theactual parameterthe address of thearray(base address) like thisDouble(arr, sz); - Lines
*(arr + i) = *(arr + i) * 2;&arr[i] = arr[i] * 2;have the same meaning
- When an
- In
Clanguage, size of array must be>=number of characters in a string+1 - This happens because if you defined an array of character like this
int main() {
char C[10];
C[0] = 'A';
C[1] = 'r';
C[2] = 'a';
C[3] = 'f';
C[4] = 'a';
C[5] = 't';
}Then we have to till the compiler that character
tis the last character in that array (as you can see thearrayis with size 10, but I only occupied 6 characters), so that we want to add a null terminator after the last character like thisC[6] = '\0';
- All functions for string manipulation in
Cexpects that strings willnullterminated (using\0)
A Rule: string in
Chas to be ended byNullcharacter
- Let's see an example
int main() {
char C[6];
C[0] = 'A';
C[1] = 'r';
C[2] = 'a';
C[3] = 'f';
C[4] = 'a';
C[5] = 't';
printf("%s",C);
}O/P:
Arafat0����
As you can there is a garbage characters afterArafatbecause we did not specify the end of that character array
- To solve this type
char C[7];
C[0] = 'A';
C[1] = 'r';
C[2] = 'a';
C[3] = 'f';
C[4] = 'a';
C[5] = 't';
C[6] = '\0'; // Add null character
printf("%s", C);- Functions like
strlen()will only count the number of characters in the array despite the size of that array (\0will not be counted), let's see
int main() {
char C[100];
C[0] = 'A';
C[1] = 'r';
C[2] = 'a';
C[3] = 'f';
C[4] = 'a';
C[5] = 't';
C[6] = '\0';
int len = strlen(C);
printf("Length: %d",len); // 6
}Note :
\0is not counted in th length of that array of strings + it counts only the characters not the size of that array
- You can also initialize an array of characters like this
int main() {
char C[100] = "Arafat";
int len = strlen(C);
printf("Length: %d",len);// 6
}VIP Note : in the above code,
\0is implicitly exists by the compiler, this means that in this case you don't have to write it
- You cannot initialize an array of character after it's declaration, something like this
int main() {
char C[100];
C = "Arafat"; // Error
// Rather
C[0] = 'A';
C[1] = 'l';
C[2] = 'i';
}- We can write it also like this
int main() {
char C[] = "Arafat"; // Without adding a size
int len = strlen(C);
printf("Size In Bytes : %d\n", sizeof(C)); // 7 Bytes
printf("Length : %d\n",len); // 6
}Note : the size in bytes of the above
arraywill be 7 , this because as we saidArafatword is6 Characters * 1 Byte = 6 Bytes+1 Byte(the implicit\0) character, so then the Total will be7
- Observe the following
int main() {
char C[6] = "Arafat";// Compilation Error
}Because the compiler will force the size of that
arraywith minimum size7(to be able to add\0), so that it will give an error
- Another way to initialize an
arrayof characters
int main() {
char C[7] = {'A','r','a','f','a','t','\0'};
}In this way we have to add the null character (
\0) as it is stored explicitly (must be writen by the developer) not implicitly (automatically by the compiler)
- Now let's play a litter with character
arrays&pointers
int main() {
char C[] = "Arafat"; // Address of 200 (A) 201 (r) 202 (a) 203 (f) 204 (a) 205 (t)
char *c1 = C;// c1 pointer will store the address of 200
printf("%c\n",c1[1]); // like saying *(c1+1) which is 'r'
c1[2] = 'z'; // like saying *(c1+2) = 'z'
printf("%s\n",C); // Arzfat
// C = c1; // Error
c1+=2; // Now address = 202
printf("%c\n",*c1); // z
}- Remember,
Arraysare always passed to function by reference, which means that we pass the base address of thatarrayto that function
// Using pointer as a parameter
void MyPrintFun1(char *c)
{
while (*c != '\0') {
printf("%c", *c);
c++;
}
printf("\n");
}
// Using an array as a parameter
void MyPrintFun2(char c[]) {
int i = 0;
while (c[i] != '\0') {
printf("%c", c[i]);
i++;
}
printf("\n");
}
int main() {
char C[] = "Arafat";
MyPrintFun1(C);
MyPrintFun2(C);
}- Note that in the pointer function, all the following are correct
while (*c != '\0')
while (c[i] != '\0')
while (*(c+i) != '\0')- You can write an array of characters like this
int main() {
char *C = "Hello"; // this string is stored as a constant in text segment in memory during compilation
C[0] = 'a'; // This will case a run-time error
printf("%s",C);
}In C, when you declare a character pointer and assign it a value using double quotes, such as char *C = "Hello";, the resulting string literal is stored in a read-only memory segment. This means that the memory location where the string is stored cannot be modified.
The compiler interpretschar* C = "Hello"aschar C[6] = {'H','e','l','l','o','\0'}
- If we have a character array & we passed it in a function, we can modify this array using this pointer like this
void MyPrintFun1(char *c)
{
c[0] = 'Z'; // Allowed
while (*c != '\0') {
printf("%c", *c);
c++;
}
printf("\n");
}
int main() {
char C[] = "Hello";
MyPrintFun1(C); // op: Zello
}- Sometimes we want a function that read a string not be able to write or modify
anything in it, to do so we will pass the parameter as
constlike thisvoid MyPrintFun1(const char *c)
void MyPrintFun1(const char *c)
{
c[0] = 'Z'; // Compilation Error
while (*c != '\0') {
printf("%c", *c);
c++;
}
printf("\n");
}
int main() {
char C[] = "Hello";
MyPrintFun1(C);
}Now we can read
C[]inMyPrintFun1()but not writing in it
- Carefully observe the following code
#include <bits/stdc++.h>
using namespace std;
int main() {
int A[2];
// A[0] -> refers to an int
// A[1] -> refers to an int
int B[2][3] = {{2, 3, 6},
{4, 5, 8}};
// B[0] -> refer to 1-D array of 3 elements
// B[1] -> refer to 1-D array of 3 elements
//int *p = B; // wrong as B will return a pointer to 1-D array of 3 elements, while p points only on an integer
int (*p)[3] = B;
}- How to access the elements
int B[2][3] = {{2, 3, 6},
{4, 5, 8}};
// All have the same Output
cout << B << " " << B[0] << " " << *B << " " << &B[0] << " " << &B[0][0] << endl; // 400 (element 2)
cout<< B+1 << " " << &B[1] << endl; // 400 + size of 1-D array of 3 elements (12) = 400 + 12 = 412 (element 4)
cout << *(B + 1) << " " << B[1] << " " << " " << &B[1] << " " << &B[1][0] << endl; // 412 (element 4)
// Can't type in the following line B[1][2] as it will return the value
cout << *(B + 1) + 2 << " " << B[1] + 2 << " " << &B[1][2] << endl; // 420 (element 8)
cout << *(*B + 1) << endl; //404 (element 3)So, For 2-D Array, Accessing an element equation is
B[i][j] = *(B[i] + j) = *( *(B+i) + j)
- Carefully observe the following code
#include <bits/stdc++.h>
using namespace std;
int main() {
int C[3][2][2] = {{{2, 5}, {7, 9}},
{{3, 4}, {6, 1}},
{{0, 8}, {11, 13}}};
int (*p)[2][2] = C;
cout << C << " " << C[0] << " " << &C[0][0] << " "; // address 800 (element 2)
cout << *(C[0][1] + 1) << " " << C[0][1][1] << " " << &C[0][0] << " "; // address of element 9
cout << *(C[1] + 1) << " " << C[1][1] << " " << &C[1][1][0] << " "; // address 824 (element 6)
}So, For 3-D Array, Accessing an element equation is
B[i][j][k] = *(B[i][j] + k) = *( *(*(B+i) + j) + k)
- Let's see the following code
// Created by Ahmed Arafat on 1/10/2023.
#include <bits/stdc++.h>
using namespace std;
// 1-D Array
void Fun1_1(int A[]) {}
void Fun1_2(int *A) {}
// 2-D Array
void Fun2_1(int B[][2]) {}
void Fun2_2(int (*p)[2]) {}
// 3-D Array
void Fun3_1(int C[][2][2]) {}
void Fun3_2(int (*p)[2][2]) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 2; k++) {
//cout<<p[i][j][k]<<" ";
cout << *(*((*p + i) + j) + k) << " ";
}
cout << "\t";
}
puts("");
}
}
// Wrong Syntax
// void Fun3_2(int ***C) {}
int main() {
int C[3][2][2] = {{{2, 5}, {7, 9}},
{{3, 4}, {6, 1}},
{{0, 8}, {11, 13}}};
int (*p)[2][2] = C;
Fun3_2(C);
}-
Application's memory can be divided into 4 segments
- Code (Text)
- Static/Global Variables
- Stack : Stores function calls & local variables
- Heap
-
Let's see the following code
#include <bits/stdc++.h>
using namespace std;
int total; // Global
int Square(int x) // x is local to Square()
{
return x * x;
}
int SquareOfSum(int x, int y) // x,y are local to SquareOfSum()
{
int z = Square(x + y); // z is local to SquareOfSum()
return z;
}
int main() {
int a = 4, b = 8;// a,b are local to main()
total = SquareOfSum(a, b);
printf("%d", total);
}-
When our program starts, operating system allocates some amount of reserved space for stack like
1MB,but the actual allocation of the stack frame happens during run time, and if our call stack goes beyond reserved memory so then this will be calledStack Over Flow, then our program will crash (can be happened during recursion without adding base condition) -
So, there is some limitation inside the stack, the memory cannot be grown once allocated to the stack, application cannot request more memory for the stack so that the allocation & reallocation in the stack follows a rule, when a function is called it is pushed into the top of the stack or removed from the top of the stack when the function stops executing
-
It is not possible to manipulate the scope of the variable if it is stored in the stack
-
Another limitation is that we need to declare a large array as local variable, we need to know the size of the array during compile time if we have the scenario where we want to decide the size of an array based on a parameter during run time it is a problem for the stack
-
Unlike stack, heap's size can vary during the lifetime of a program & there is no set rule for allocation and reallocation of memory, a programmer can totally control
-
how much memory to use in a heap and to what time he wants to keep the data in the memory, and heap can grow as long as there is available space in the system, this is also a dangerous thing in heap that we want to be really careful while using heap, heap also can be called
free pool of memory -
How heap is implemented is decided by operating system, compiler. It is something that can vary depending on the computer architecture
-
Heap is also called
Dynamic Memory& using the heap is referred to asDynamic Memory Allocation -
To use dynamic memory in
CLanguage we need to use 4 functionsmalloc()calloc()realloc()free()
-
To use dynamic memory in
C++we need to know 2 operatorsnewdelete
-
Note: in reality the size of the stack is decided by the operating system & the compiler
-
Now lets allocated some memory in heap
int main() {
int a;// stored in stack
int *p;
p = (int*) malloc(sizeof(int));
*p = 10;
// now p will point into another block in heap
// the previous block still exists in heap
// to deallocate the memory in heap that is referred by pointer p
// type free(p);
free(p);
p = (int*) malloc(sizeof(int));
*p = 20;
}
malloc(sizeof(int))will return a void pointer to the starting address of block allocated in heap, so we need to typecast it like this(int*). Now it will return the address of 200
-
It is the responsibility of the programmer to clear anything allocated by him in the heap, and it is not needed anymore
-
To store an array in the heap in
CLanguage type
int main() {
int *p;
// 4 * 20 = 120 Byte of memory will be allocated in heap
// and now `p` contains the base address of that array
p = (int*) malloc(sizeof(int) * 20);
p[0] = 10;
p[1] = 20;
*p = 11; // override 10
*(p+1) = 21; // override 20
}- To write the above code in
C++
int main() {
int *p;
p = new int;
*p = 10;
delete p;
p = new int[20];
p[0] = 10;
p[1] = 20;
*p = 11; // override 10
*(p + 1) = 21; // override 20
delete[] p;
}- Let's now see some functions that dynamically allocate memory in
CLanguage - Allocate Block of memory :
malloc()- signature/definition of it is like :void * malloc(size_t size)- Its parameter asks you for size of memory blocks in bytes & return the address of the first Byte of block that is allocated in heap
- It itself is a generic function that allocate some size of memory, it does not care
whether you're allocating this memory to store
charorintor any other datatype. - It simply returns a
void pointerto the starting address
calloc()- signature/definition of it is like :void * calloc(size_t num, size_t size)- This means that if you want to allocate an array of size 3 you will type
(int*)calloc(3,sizeof(int)) - One main difference between
malloc&callocis thatmallocdon't initialize the bytes with any values, so there might be garbage values, whilecallocinitialize all allocated Bytes to 0
- This means that if you want to allocate an array of size 3 you will type
realloc()- signature/definition of it is like :void* realloc(void *ptr,size_t size)- The first argument is a pointer to starting address of existing block & the second argument is the size of new block
- Deallocate Block of memory :
free()
size_tis a datatype that stores positive integer only likeunsigned, the size cannot be 0 or any negative value
Remember: you cannot de-referencing a void pointer as it is a generic pointer type, so that we have to typecast it
In
reallocfunction, if the new size is greater than the old one, here the compiler see if it can extend the old block it will do that otherwise it will create another block of memory with new size and then copy all the bytes of the old block to it & then free the old block
- Now Let's write some code
int main() {
int n;
printf("Enter size of the array\n");
scanf("%d", &n);
int *arr = (int *) malloc(n * sizeof(int));
for (int i = 0; i < n; i++) {
arr[i] = i + 1;
}
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
}- Remove initialize loop to see the value of the array
int main() {
int n;
printf("Enter size of the array\n");
scanf("%d", &n);
int *arr = (int *) malloc(n * sizeof(int));
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
}O/P:
-957605056 324 -957611696 324 0, as you can see it is garbage value
- Now use the function
calloc()to observe how it initializes the bytes to zero
int main() {
int n;
printf("Enter size of the array\n");
scanf("%d", &n);
int *arr = (int *) calloc(n,sizeof(int));
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
}O/P:
0 0 0 0 0
- Now let's see if we used
free()function then we tried to access that array
int main() {
int n;
printf("Enter size of the array\n");
scanf("%d", &n);
int *arr = (int *) calloc(n, sizeof(int));
free(arr);
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
}O/P:
-199091392 707 -199098032 707 0
As you can see we can access that array even if the memory is freed, this is because pointer
arrstill refer to the same block ( it refers to the base address of the array) even if the memory is freed (as you stored in that pointer the address)
We can also access the array even after usingfree()function like thisarr[2] = 10;but this line may cause crash in some machines
To prevent access that block of memory again typearr = NULL;, Now we cannot refer to that array anymore as the pointerarrnow is referring toNULL
So, the purpose of usingfree()is that the compiler will be able to reallocate the released memory again in any other variable
- Now let's use
realloc()function
int main() {
int n;
printf("Enter size of the array\n");
scanf("%d", &n);
int *arr = (int *) malloc(n*sizeof(int));
for (int i = 0; i < n; i++) {
arr[i] = i+1;
}
int *arr2 = (int*) realloc(arr,n*2*sizeof(int));
printf("Address of arr %d , Address of arr2 %d\n",arr,arr2);
for (int i = 0; i < n*2; i++) {
printf("%d ", arr2[i]);
}
}
/*
O/P:
Address of arr -9430176 , Address of arr2 -9430176
1 2 3 4 5 -1163005939 -1163005939 -1163005939 -1163005939 -1163005939
*/As you can see in this case, the old block of code is extended to store more elements, that's because the address of
arr2is like addressarr
to reduce the array's size we can typeint *arr2 = (int*) realloc(arr,(n/2)*sizeof(int));
Note #1:arr = (int*) realloc(arr,0);is like sayingfree(arr)
Note #2:int *arr = (int*) realloc(NULL,n*size(int));is like sayingint *arr = (int*) malloc(n*size(int));
-
Pointers are just another data type, so we can use them as a return type for a function
-
Let's see again an example of passing a parameter by value
int Add(int a, int b) // Called function
{
int c = a + b;
return c;
}
int main() { // Calling function
int a = 2 , b = 6;
printf("Address of a in main : %d\n",&a);
// value of a in main() is copied to value of a in Add()
// value of b in main() is copied to value of b in Add()
int c = Add(a,b);// Call by value
printf("Sum = %d\n",c);
}- What if we want to make function
Add()returns the address ofcrather than returning the value
int* Add(int *a, int *b) // Called function
{
int c = *a + *b;
return &c;
}
int main() { // Calling function
int a = 2 , b = 6;
int *c = Add(&a,&b);// Call by reference
printf("Sum = %d\n",*c);
}the following code may or may not work depending on your compiler, but the following code will not work definitely
void PrintHello()
{
printf("Hello Arafat\n");
}
int* Add(int *a, int *b) // Called function
{
int c = *a + *b;
return &c;
}
int main() { // Calling function
int a = 2 , b = 6;
int *c = Add(&a,&b);// Call by reference
PrintHello();
printf("Sum = %d\n",*c);
}Why?, Because
cvariable is a local variable to the functionAdd(), which means that is has been created inside its stack frame, so we are returning its address which located inside its frame, but after the functionAdd()is terminated & the other functionPrintHello()is called, its stack frame will be created instead of the terminatedAdd()function, so that the address ofcmay during this call be overridden as it is now in stack frame ofPrintHello()function, so that it won't work & if it works in some compilers then the value of address ofcwill be garbage value
- To solve this problem we will have to create the variable
cin heap not stack like this :
void PrintHello()
{
printf("Hello Arafat\n");
}
int* Add(int *a, int *b) // Called function
{
int *c = new int; // allocate c in heap
// In C Language
//int *c = (*int) malloc(sizeof(int)); // allocate c in heap
*c = *a + *b;
return c;
}
int main() { // Calling function
int a = 2 , b = 6;
int *c = Add(&a,&b);// Call by reference
PrintHello();
printf("Sum = %d\n",*c); // O/P : 8
}- Bottom line, it is okay to pass a pointer (address) from bottom to top
(from
main()toAdd()), because it is a guarantee thatmain()is still occupying the stack, but passing a pointer (address) from top to bottom (fromAdd()tomain()) is wrong asAdd()sooner or later will be deallocated from the memory and its stack frame might be replaced by any other called function
- Function pointers is used to store the address of functions
- So, pointers can point to data & can point to function
- We can use a pointer to function to dereference & execute that function
- But the question here is what really is the address of a function & what is the use-cases ?
-
In memory the function will be one contiguous block of some instructions, the address of a function which is also called
entry pointof a function will be the address of first instruction in that function (address 206 in below picture) -
when we say function pointers, we mean pointers that store the starting address or
entry pointof block of memory contains all instructions for that function -
Let's see the syntax of writing the function
pointers
int Add(int a, int b)
{
return a + b;
}
int* Add1(int a, int b)
{
int *c = new int;
*c = a+b;
return c;
}
int main() {
//int is the return type of the function this pointer will refer to
//(int,int) is the data type of the function this pointer will refer to
int (*p)(int,int);
// the return type of the function this pointer will refer to is pointer to int
int* (*p2)(int,int);
p = &Add;
// p = Add; // function name will return the address of it
int c = (*p)(5,10); // de-referencing and executing the function
c = p(5,10); // correct
printf("Sum = %d",c);
}- Let's observe some Compilation Errors with function
Pointers
int Add(int a, int b)
{
return a + b;
}
int main() {
void (*p)(int,int);
p = &Add; // Compile Error as Add() returns int not void
}- Another example :
int Add(int a, int b)
{
return a + b;
}
int main() {
int (*p)(int);
p = &Add;
int c = p(2,3); // Compilation Error as pointer p refers to a function that takes only one parameter not two
}- Let's practice writing a function
pointerto avoidfunction
void PrintHello()
{
printf("Hello Arafat\n");
}
int main() {
void (*ptr)();
ptr = PrintHello;
ptr(); // (*ptr)();
}- Let's practice writing a function
pointerto avoidfunction that takeschar *as a parameter
void PrintHello(char *c)
{
printf("Hello %s\n",c);
}
int main() {
void (*ptr)(char *);
ptr = PrintHello; // ptr = &PrintHello;
ptr("Arafat"); // (*ptr)("Arafat");
}In Function
Pointers& Callbacks part, we will see the real use-cases of functionpointer
#include <bits/stdc++.h>
using namespace std;
void Swap(int *a, int *b) {
int c = *a;
*a = *b;
*b = c;
}
void BubbleSort(int *arr, int n) {
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n - 1 - i; j++) {
if (arr[j] > arr[j + 1]) Swap(&arr[j], &arr[j + 1]);
}
}
}
int main() {
int arr[] = {5, 4, 3, 2, 1};
int n = sizeof(arr) / sizeof(arr[0]);
BubbleSort(arr, n);
for (int i = 0; i < n; i++) printf("%d ", arr[i]);
}#include <bits/stdc++.h>
using namespace std;
void Swap(int *a, int *b) {
int c = *a;
*a = *b;
*b = c;
}
int Compare(int a, int b)
{
return a > b ? 1 : 0 ;
}
void BubbleSort(int *arr, int n, int (*ptr)(int,int)) {
for (int i = 0; i < n; i++) // 5 4 3 2 1
{
for (int j = 0; j < n - 1 - i; j++) {
if (ptr(arr[j],arr[j + 1]) > 0) Swap(&arr[j], &arr[j + 1]);
}
}
}
int main() {
int arr[] = {5, 4, 3, 2, 1};
int n = sizeof(arr) / sizeof(arr[0]);
BubbleSort(arr, n,Compare);
for (int i = 0; i < n; i++) printf("%d ", arr[i]);
}#include <bits/stdc++.h>
using namespace std;
void Swap(int *a, int *b) {
int c = *a;
*a = *b;
*b = c;
}
int CompareASC(int a, int b)
{
return a > b ? 1 : 0 ;
}
int CompareDESC(int a, int b)
{
return a < b ? 1 : 0 ;
}
int CompareABS(int a, int b)
{
return abs(a) > abs(b) ? 1 : 0 ;
}
void BubbleSort(int *arr, int n, int (*ptr)(int,int)) {
for (int i = 0; i < n; i++) // 5 4 3 2 1
{
for (int j = 0; j < n - 1 - i; j++) {
if (ptr(arr[j],arr[j + 1]) > 0) Swap(&arr[j], &arr[j + 1]);
}
}
}
int main() {
int arr[] = {5, 4, 3, 2, 1};
int n = sizeof(arr) / sizeof(arr[0]);
BubbleSort(arr, n,CompareDESC);
//BubbleSort(arr, n,CompareASC);
//BubbleSort(arr, n,CompareABS);
for (int i = 0; i < n; i++) printf("%d ", arr[i]);
}int MyCompare(const void *a, const void *b)
{
int A = *((int*)a); // Typecasting to int then getting the value
int B = *((int*)b); // Typecasting to int then getting the value
// it will return a positive number if A ranked higher than B (In this case it will swap as returned number is positive)
// it will return a negative number if A ranked lower than B
// it will return ZERO if A has the same rank as B
return A-B;
//return B-A;
//return abs(A)-abs(B);
}
int main() {
int arr[] = {5, 4, 3, 2, 1};
int n = sizeof(arr) / sizeof(arr[0]);
qsort(arr,n,sizeof(int),MyCompare);
for (int i = 0; i < n; i++) printf("%d ", arr[i]);
}VIP Note: The advantage of using function pointer in the above codes is that I'm not modifying any lines in the function
BubbleSort(), the only thing I'm changing is while calling this function inmain()like thisBubbleSort(arr, n,CompareDESC);, I'm just changing the name of passed function so then in this case the function pointerptrwill refer to the passed function allowing me to just call that function using my pointerptr(again without modifying any code in functionBubbleSort()), this will help me making my code reusable & becoming aclean code`
-
This phenomenon happened due to improper use of dynamic memory (memory in the heap)
-
As we said before we can free memory in heap using function
free()inCLanguage & operatordeleteinC++ -
Let's create a simple game called
Simple Betting Game -
The computer will shuffle
Jack Queen Kingcards -
The player has to guess the position of the
Queen -
If he wins, he takes
3 * bet -
If he looses, he will lose the bet amount
-
Player has
100 L.Einitially
- Let's write the code of this game :)
// Created by Ahmed Arafat on 1/10/2023.
#include <bits/stdc++.h>
using namespace std;
int cash = 100, bet;
void play() {
printf("Shuffling The Cards .....\n");
Here:
printf("Please Enter The Position Of The Queen 1,2 or 3");
int user_pos, pos;
scanf("%d", &user_pos);
if (!(user_pos >= 1 && user_pos <= 3)) {
printf("Please Enter A Valid Number\n");
goto Here;
}
user_pos--;
srand(time(NULL));
pos = rand() % 3;
if (pos == user_pos) {
printf("You Win ! Congratulations\n");
cash += 3 * bet;
} else {
printf("Wrong Guessing The Position Of The Queen Was %d\n", pos + 1);
cash -= bet;
}
printf("Your Cash Now : %d L.E\n", cash);
}
int main() {
while (cash) {
printf("What Is Your Bet?\n");
scanf("%d", &bet);
if (bet > cash) printf("You Only Have %d L.E\n", cash);
else if (bet <= 0) printf("Enter A Positive Number Please\n");
else
{
play();
printf("\n*************************\n");
}
}
}- You can modify the function
play()like this
void play() {
char Cards[] = {'Q', 'K', 'J'};
printf("Shuffling The Cards .....\n");
Here:
printf("Please Enter The Position Of The Queen 1,2 or 3");
int user_pos, pos;
scanf("%d", &user_pos);
if (!(user_pos >= 1 && user_pos <= 3)) {
printf("Please Enter A Valid Number\n");
goto Here;
}
srand(time(NULL));
for (int i = 0; i < 10; i++) {
int x = rand() % 3;
int y = rand() % 3;
int temp = Cards[x];
Cards[x] = Cards[y];
Cards[y] = Cards[temp];
}
if (Cards[--user_pos] == 'Q') {
printf("You Win ! Congratulations\n");
cash += 3 * bet;
} else {
printf("Wrong Guessing \n");
cash -= bet;
}
printf("Your Cash Now : %d L.E\n", cash);
}The memory consumption from the above code will be the same as time run, but if we modified it to be something like this
void play() {
char *Cards = (char *) malloc(3 * sizeof(char));
Cards[0] = 'Q';
Cards[1] = 'K';
Cards[2] = 'J';
......
......
}Each time we call function
play()we allocate a memory in heap, which will cause a memory lack because we are not deallocating this block of memory, to solve this we have to writefree(Cards)at the end of the function `play()


