POINTERS
Pointers are special variables that are responsible to store the l-value of a variable.
Let us clear of conception about l-value and r-value.
Well, r-value stands for real-value.
On the other hand, l-value stands for location-value.
Suppose we consider the following declarations as given below
i) int a=2;
in the above initialization, a is a variable that refers to a value of 2.
In other words, a memory address is created at a random memory location with name as 'a' and the value stored in that memory location is 20.
Now as shown here a is the name, 20 is the r-value and 400 is its address or l-value.
As now we are through with these let's dive into the depths of next syntax
ii) int a=2
int *p=&a
Now see in the first statement a variable with name a points to a value 20 with l-value as 400. In the second case, we have a pointer with name p that points to the address of variable 'a' or 400 and stores it into a separate memory address i.e. 600.
Now we shift to our second important topic under pointer.
POINTER AND ARRAY
An array is a contiguous memory allocation where numbers are stored in a continuous form.
For example suppose, we are given an array
int a[]={5,10,7,9,8};//line 1
int *p=a;//line 2
int *p=&a;//line 3
What is the difference between the 3 lines?
line 1 initializes an array with certain values
line 2 is not an error. In the case of an array, the name of the array itself becomes the pointer base address.
line 3 is also fine. It states that a container is created which stores the total array a.
Now let’s
look at certain notations in a pointer.
int a[2]
int
*(a+2)
int 2[a]
Which of
the above is correct?
Though it
may not look but all three are accepted and are correct.
The first
statement is obviously correct.
a[2]=*(a+2)
For any
pointer operation
*(a+i)=a[i]
where i is a constant.
Now
*(a+i)=*(i+a)
a[2]=2[a]
Thus, all
are the same.
Q) if
A={2,5,6,7,8,0}
Find i)
A[2] ii)3[A]
Pointer
addition
Now pointer addition is somewhat different from
normal addition.
Suppose we are give a pointer with name A and asked
to find the value of
*(A+2)
The * operator is address of operator. Well in this
case our address value is calculated as
Resultant address= Base Address(A)+
data_type(A)*i…..here * means normal multiplication.
Now that we are through this lets take the previous
question and add a subpart to it.
iii) Find *(A+1)
Storage
of pointers
Since we are on the context of datatypes let’s make
this very clear that every pointer irrespective of its type occupies 2B.
Although certain books say that since pointer always store addresses and
addresses are necessarily numbers. Numbers are of 2B, hence, each pointer
occupies 2B.
So, just solve the below sum
i)
sizeof(void *d)-sizeof(int *f)*sizeof(char *h)
ii)
sizeof(void *d)-sizeof(int *f)*sizeof(char *’c’)
Well
both looks similar but they are not. The first one gives -2 as sizeof() returns
size of a variable or datatype in Bytes(B).
The second one is an error.
In
pointers we can only pass variables or memory addresses but not a constant like
‘c’.
Memory allocation of Pointers
To
understand the memory allocation, we need to understand how the memory looks
like.
Any pointer when not allocated memory resides in the heap
memory.
There are generally 2 types of methods to allocate memory.
Static allocation: This is the type of allocation that take
place during compilation.
Dynamic allocation: This is the type of allocation in which
the amount of memory to be allocated is known during the runtime. These values
are stored in the Heap section.
The space in between heap and stack memory is the freely
expandable space. Heap expands in the downward direction and Stack expands in
the upward direction. At the time when both collides and there are no space for
any memory allocation then it becomes crashing.
Dangling
pointers
Dangling pointers arise during object destruction when an object that has an incoming reference is deleted or deallocated, without
modifying the value of the pointer, so that the pointer still
points to the memory location of the deallocated memory.
Suppose we have a pointer that was pointing to a certain
value. Now if that value gets deleted somehow but this pointer does not change
its r-value leading to a dangling pointer situation.
Let’s see this with an example
int a=30;
int *p=a;
int *s=Null;
s=p;
delete(p);
print(s)// we are referring to s and this is not an error but
the value is irrelevant. Hence this leads to dangling error.
Why the notion of the pointer may become dangerous?
Pointers may appear to be harmless but it can do some great damages.
Some of these are mentioned below:
Suppose if the pointer takes any system address as its value
then it can cause those system values to change and we may face problems.
Let’s take an example to understand. The world’s most simple
virus can be made by just checking the address of Caps-Lock and assigning it to
a pointer. Now definitely Caps-lock may be either ON/OFF denoted by 1/0
respectively. Now suppose if we take the value and perform bitwise OR operation
of the above mentioned Caps-Lock value with 1, the answer will always remain 1.
Hence, we cannot write any C, C++, Java or Python codes. Because they are all
case sensitive.
Remedy: Always initialize your pointers to NULL and
do not allow the pointer to randomly take up a value.
int*p=NULL;
Here we are not allowing the pointer p to take up
any garbage value as data.
2)
Another prominent case is when suppose, we dynamically
allocate some memory, but forget to free the memory then our memory remains
allocated leading to increase in Heap memory and may sometime go on to crash
completely.
Remedy: The only remedy in such situation is that
dynamic allocation and deallocation must take place anywhere when allocation is
needed.
Because of these problems certain programming languages like Java,
Kotlin has removed pointers totally.
Self-Referential structure
Suppose the definition of a structure of a structure is given as below
struct demo1 struct
demo2
{ {
int a; int
b;
struct demo2 p; struct
demo1 r;
}k; }m;
Now suppose when object of struct demo1 is to be created, it calls
creation of demo2 struct which in turn calls calling of object of demo1 and
this process continues till the total memory is exhausted.
How to solve this problem?
Well, again pointers coming to our rescue. Let us see how
struct demo1 struct
demo2
{ {
int a; int
b;
struct demo2 *p; struct
demo1 *r;
}k; }m;
Now when we create an object of demo1, the object need not call object
formation of demo2. As the size of any pointer is 2B, size of demo1 can be
easily calculated as 4B. Similarly for demo2 it is 4B.
This is how pointer to an object of another structure solves the
crashing problem. Although the above structure is stable but definitely this is
not a self-referntial structure.
A self-referential structure is one which calls another
object of same type in the same structure. Let us see an example.
struct Tree
{
int key;
struct Tree *left,*right;
}root;
Here we find that inside the structure, same structure is called. These
are self referential structure.
SOME COMPLEX FUNCTION PROTOTYPES
1)
int f(float)
>>>A function with name f taking a float value and returns an
integer value
2)
int *f(int)
>>>A
function taking an integer value and returning an int type pointer
3)
int (*f)(int)
>>>A
pointer to function referred to by ‘f’ which takes an integer argument and
returns an integer value
4)
void (*f[10])(int)>>An array of pointer to function
which takes an int value as argument and returns no value.
5)
Int *(*a[5])(int)
>>>An array of 5 pointer to function which takes an integer
value as argument and returns a pointer to integer in each case.
6)
float ***f(double ***)>> A function
that takes pointer to pointer to pointer of double argument and returns pointer
to pointer to pointer to float as return value.
7)
char (*f)(int (*)[3])>>>>A pointer to function
referred by F that takes a pointer to an array of size 3 as argument and
returns a char type data.
8)
Char(*(*f[3])(int))[5];>>>An array
of 3 function pointers which takes an integer value and returns a pointer to
array of size 5 of char type
9)
Float(*(F(int *)))[3]>>>>A function that takes an
integer pointer as argument and returns a pointer of an array of size 3 of
float type.
10) Float *F(int *)[3]>>>>>>A
function which takes an integer pointer and returns a pointer array of float
type
11) Char (*(*F())[3])(int)>>>A
function which takes no argument and returns a pointer of which in turn is a
function of array of size 3 pointer which takes an integer argument and return
a char value.
12) Void (*F)(int,void*(*)())>>>A pointer
to function ‘F’ that takes an integer and a pointer of function that returns a
void pointer which in turn returns no value
13) Int **(*F)(int
**,int **(A)(int **,int **))>>>A pointer to a function referred by ‘F’ which
takes a pointer to pointer of integer and a function A which takes 2 pointer to
pointer of integer as arguments and returns pointer to pointer of integer which
in turn returns a pointer to pointer of integer.




No comments:
Post a Comment