0% found this document useful (0 votes)
64 views

Abhijit A.M.: Pointers in C

This document discusses pointers in C. It explains that pointers store addresses and have a type like int*, char*, etc. Pointer size is determined by the compiler. Operations on pointers include dereferencing (*), referencing (&), addition/subtraction of an integer, subtraction of two pointers, and treating pointers like arrays using []. Arrays decay to pointers when passed to functions, with the array name passing the address of the first element. Dynamic memory is allocated at run time using functions like malloc().

Uploaded by

Sanket Patil
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
64 views

Abhijit A.M.: Pointers in C

This document discusses pointers in C. It explains that pointers store addresses and have a type like int*, char*, etc. Pointer size is determined by the compiler. Operations on pointers include dereferencing (*), referencing (&), addition/subtraction of an integer, subtraction of two pointers, and treating pointers like arrays using []. Arrays decay to pointers when passed to functions, with the array name passing the address of the first element. Dynamic memory is allocated at run time using functions like malloc().

Uploaded by

Sanket Patil
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 72

Pointers in C

Abhijit A.M.
[email protected]

(C) Abhijit A.M.


Shared under Creative Commons Attribution
Sharealike International License V3.0
Pointer

Pointer is a variable which can store
addresses

Pointer has a “type” (except void pointer)

e.g. int *p; char *cp; double *dp;

Here p, cp, dp are respectively pointers to
integer, character, and double

Size of pointer is decided by compiler
Operations on (and related to)
Pointers

&
*
=
+int -int
-
[]
Operations related to pointers: &
& fetches the address of variable
Called Referencing operator
int *p, i; Here, &i is address of i
p = &i; RHS is address of integer, LHS is
variable which can store address
of integer
Diagram of this operation is shown
on left side
No need of assuming some value
p i for address (e.g. address 1028).
Just the diagram is sufficient to
understand the concept
Operations on pointers: *
* fetches the value stored at
int *p, i, j; a given address
i = 10; Called dereferencing
operator
p = &i;
*p : here “value of p” is itself
j = *p; an address (of i), so *p is
10
value stored at “value of
j p” that is i
10
Diagram's make it easy to
p i understand, *p is simply
the contents of box p
points to
Operations on pointers: *
int *p, i, j; char *cp, c,
d; What is the difference
i = 10; c = 'x';
between * in the two
p = &i; cp = &c;
codes?
j = *p; d 10= *cp; The *p fetches the value
at given address in
10 j sizeof(int) bytes, while
p i *cp fetches the value
at given address in
'x'
sizeof(char)=1 bytes
'x' d
* works based on the
cp c size of the type!
Operations on pointers: =
int *p, i, *q; Pointers can be copied
i = 10; Can arrays be copied?
p = &i; Thumb rule:
q = p; After pointer copy, both
pointers point to same
10 location
p i q Common Mistake
One pointer pointing to
10
another

p i q
Operations on pointers: +- int
int *p, a[4],
C allows adding or
*q;
substracting an int from a
p = &a[1]; pointer!
q = p + 2; Weird, but true!
e.g. int *p, n; p + n;
*q = 40;
a 0 1 2 3 The result is a pointer of the
40 same type
The resultant pointer points
n type locations
ahead( for +) or before
p q
( for -)
A type location is equal to
Problems: Draw diagrams for the
code
int main() { 
int main() { 
    int *p, *q, a[3], 
    int *p, *q, a[3], b;  b; 
    a[0] = 10; b = 2;     a[0] = 10; b = 1;
    p = &a[1];     p = &a[3];
    q = p + 1;     q = p ­ 3;
    p = q – b;      p = q + 1; 
    *p = 30;      *(q + 1) = 30; 
}     *(p – 1) = 20;
}
Operations on pointers:
substracting two pointers
int *p, a[4], *q, x; Two pointers of the
p = &a[1]; same type can be
q = p + 2; substracted
Result type is int
x = q – p;
Result value = no. Of
a 0 1 2 3 elements of
2 sizeof(type) between
two pointers
x
Logically derives from
adding/substracting int
p q to pointers
Operations on pointers: [ ]
int *p, a[4], *q;
Interestingly, C allows [ ]
p = &a[1];
notation to be applied
p[2] = 20; to all pointers!
p[-1] = 10; You must be knowing
that [ ] is normally used
a 0 1 2 3 for arrays

10 20 p [ i ] means * (p + i)
P is a pointer and i is an
integer (or i is a pointer
and p is a pointer is
p also allowed)
A peculiar thing about arrays

Name of an array is What do the following
equivalent to the address
of (the zeroeth element) the mean?
array Int a[3] = {1, 2, 3}, *p;
int a[3];
a + 1;
Now
a means &a[0]
*(a + 1);

Because it's an address, it
p = (a + 2)
can be stored in a pointer Exception: when
int a[3], *p;
passed to sizeof()
p = a;
a 0 1 2 3 operator, the name is
not the address
p
Pointer as if it was an array

Combine the int a[3], *p;
concepts of p = a;

Pointer arithmetic p[2] = 10;
(+- int)

[ ] notation for Here we are using p as
pointers if it was an array

Array name as name
address of array Possible only if p was
pointing to array base
a 0 1 2 3
10

p
Pointers != Arrays

Array is a continuous Pointer is a variable that
collection of elements can store an address
of one type.
Pointers can be of various

Array has name, the types
name also stands for
[ ], = , +- int, substraction
the address of the
are operations on
array
pointers

[ ] is allowed operation
Pointers can be reassigned
on arrays
to point to different

Array name can't be addresses
reassigned to another
address
Arrays as arguments to functions
char f(char *c) { Here actual argument
return c[0]; is ‘arr’

} Name of array is
address of array, that
int main() { is &arr[0]
char arr[16], x; Address of char
x = f(arr); So formal argument is
} char *
2-d array as argument to function
char f(char **c) { char f(char c[][6]) {
return c[0][2]; return c[0][2];
} }
int main() { int main() {
char arr[16][6], x; char arr[16][6], x;
x = f(arr); x = f(arr);
} }
Which one is correct?
You can answer by just applying the rules we learnt
2-d array as argument to function
char f(char (*c)[6]) { char f(char c[][6]) {
return c[0][2]; return c[0][2];
} }
int main() { int main() {
char arr[16][6], x; char arr[16][6], x;
x = f(arr); x = f(arr);
} }
Which one is correct?
(The second one is same as on the earlier slide)
You can answer by just applying the rules we learnt
Dynamic Memory Allocation
Concept of (Binding) “Time”

Compile Time

When you are running commands like
cc program.c -o program

Load time

After you type commands like
./program
Before the main() starts running

Program Run Time

After you type commands like
./program
When the main() of the program starts running, till it exits

Function Call Time

Interval between the call of a function and before the called function starts
running. Part of “Run Time”.
Lifetime of variables and Memory
Allocation int g;

Global Variables, Static Variables (g, t, static int t = 20;
and s here)
int f(int a, int b) {

Allocated Memory at load time
int x, y;

They are alive (available) till the program
exits static int s = 10;

Local Variables, Formal Arguments (i, j, x = a + b + 5 + g + s;
k in main; a, b, x, y in f) return x;

Allocated Memory on function call }

They are alive (available) as long as int main() {
function is running
int i, j, k, *p;

Dynamically allocated memory (Run
g = 10; i = 20; j = 30;
time allocation)

Allocated on explicit call to functions like p = malloc(8);
malloc() k = f(i, j);

Man pages
Understanding library functions:

Red the manual pages, using ‘man’ command

> man sqrt 
> man 3 printf
> man 3 open
# see section 3 of man pages for  C 
library functions
malloc()

malloc() is a standard C library function for
allocating memory dynamically (at run time)

#include <stdlib.h> for malloc()

Function prototype

Run “man malloc” to see it
void *malloc(size_t size);

size_t is a typedef in stdlib.h

size_t is unsigned long

Reads a number, allocates those many bytes and
returns the address of allocated memory (zero'th byte)

Additional info: malloc() gets the memory from the
void *
Void pointers can be copied

A void pointer is a
void *p, *q; int a; p = &a; q = p;
typeless pointer;
q also stores address of a now

Pure address

No type --> No “size” The Dereferencing operator
information about the has no meaning when
type applied to a void pointer

You can declare a void void *p; int a; p = &a;
pointer What does *p mean now? *
Needs “size” of the type for
void *p, *q
its work. Void pointers have

Void pointer can store no type and so no size
information associated with
any address it.
void *p; int a; p = &a; char Note: [ ] is also dereferencing
malloc()

malloc() returns a “void *”

Returns a pure address

This address can be stored in a “void *” variable

This address can also be stored in any pointer
variable

Suppose we do

void *p; p = malloc(8);

Now what meaningful operations can we do with
the malloced memory? --> only copy!

So normally return value of malloc is stored
malloc()
int *p; This code allocates 8
p = malloc(8) bytes and then
pointer p will point to
the malloced memory
This code can result in
a “warning” because
we are converting
“void *” to a “int *”
with '='
malloc()
int *p; This code does away
p = (int *) malloc(8); with the warning as
we are converting the
“void *” into “int *”
using explicit type
casting
Suppose size of integer
was 4 bytes, then
what does this code
mean?
malloc()
int *p; p[0] means *(p + 0) that is
*p
p = (int *) malloc(8);
p[1] means *(p + 1) where

p points to 8 byte
(p + 1) is a pointer 4
location
bytes ahead of p

However now, *p means
Using this trick we are
deferencing in “4 bytes” treating the 8 bytes as if

as size of int is 4 bytes it was a 2 integer array !

P+1
p
0 1 2 3 4 5 6 7

<---------p[0]--------><---------p[1]-------->
malloc()
int *p; p[0] means *(p + 0) that is
*p
p = (int *) malloc(8);
p[1] means *(p + 1) where

p points to 8 byte
(p + 1) is a pointer 4
location
bytes ahead of p

However now, *p means
Using this trick we are
deferencing in “4 bytes” treating the 8 bytes as if

as size of int is 4 bytes it was a 2 integer array !

p 0 1
Malloc(): Allocating arrays
dynamically
int *p; Use of sizeof(int) here
makes sure that the
p = (int *) malloc( code is portable,
sizeof(int) * 4); appropriately sized array
will be allocated

We can allocate arrays of irrespective of size of
any type dynamically
integer
using malloc()
Code on earlier slide
assumes 4 byte integer
This code allocates array
of 4 integers
Can be acessed as p[0],
p[1], p[2] and p[3]
malloc(): Allocating array of
structures
typedef struct test { This code allocates an
int a, b; array of 4 structures

double g; p points to the array of


structures
}test;
p[0] is the 0th structure,
test *p; p[1] is the 1st
p = (test *) malloc( structure ...
sizeof(test) * 4); p[0].a, p[1].g is the way
to access the inner
elements of
Homework

Write code using malloc() to

Allocate an array of 10 doubles

Read n from user and allocate an array of n shorts

What does the following code mean?
int *p; p = malloc(9); // suppose sizeof(int) is 4
Note: No need to write this sort of code in this
course!

What does the following code mean?
int *ip; Char *p; p = malloc(8); ip = p; p[0] = 10;
Note: again, no need to write code like this in this
course!
free()

free() will give the malloced memory back

malloc() and free() work together to manage
what is called as “heap memory” which the
memory management library has obtained
from the OS

Usage
void free(void *p);

free() must be given an address which was
returned by malloc()

Rule: Every malloc() must have a
Pointer to Pointer
int **pp, *p, p;
pp = (int **) malloc(sizeof(int *) * 4);
pp[1] = (int *)malloc(sizeof(int) * 3);

A pointer to pointer, is essentially a pointer!

Can store an address, has a type

The type is “pointer to pointer” so all * or [ ] operations
work with sizeof(pointer) which is typically 4 bytes

The code above allocates an array of pointers, and then
allocates an array of integers.
pp 0 1 2 3
2-d array with pointer to pointer
#include <stdlib.h> 0 1 ...................... 15
pp 0 1 2 3
#define N 4
int main() {
int **pp, i, j;
pp = (int **) malloc
0 0 0 1 0 2
0 3
(sizeof(int *) * N) ;
for(i = 0; i < N; i++)
1 1
1
2 1 3 1 4
pp[i] = (int *)
malloc(sizeof(int) * N); 2 3 4 5
for(i = 0; i < N; i++) 2 2 2 2

for(j = 0; j < N; j++) 3 3 4 3 5 3 6


3
pp[i][j] = i + j;
return 0;
}
2-d array with pointer to pointer
#include <stdlib.h> How does pp[i][j] work here?
#define N 4 pp[i] is *(pp + i) which is the
int main() { pointer at the i'th location in
int **pp, i, j; the array of pointers
pp = (int **) malloc allocated
Here * works with size = sizeof a
(sizeof(int *) * N) ;
pointer
for(i = 0; i < N; i++)
pp[i][j] is *(pp[i] + j)
pp[i] = (int *)
Since pp[i] is a integer pointer, so
malloc(sizeof(int) * N); here * works with sizeof(int)
for(i = 0; i < N; i++) pp[i] is the address of array
for(j = 0; j < N; j++) (indicated in red), so pp[i] + j is
pointer to the j'th element of
pp[i][j] = i + j; that array
return 0;
Self Referential Pointers in Structures
Self Referential Pointers

“Self Referential Pointer” is a kind of a
misnomer

C allows structures like this
struct test {
int a;
struct test *p;
};

The pointer p, can point to any variable of
the type “struct test” (or be NULL)
Self Referential Pointers
p

Consider following code
typedef struct test { x
a q
int a;
struct test *p; p
y
}test;
a
test x, y, *p, *q;
p
p = &x;
x.p = &y;
q = &y;
Self referential structures allow us to create a
y.p = &y;
variety of “linked” structures of data
-> notation
typedef struct test { (*x) is the entire structure to
int a; which x points
struct test *p (*x).a is the variable 'a' in that
structure
}test;
x->a is another notation for
test m, n, *x; x
(*x).a
m.a = 20;
m
x = &n; a 20
n
(*x).a = 40; p
a 40
50
x->a = 50;
p
x->p = &m;
2 Common Problems involving Pointers:

(Dereferencing) Dangling Pointers


And
Garbage Memory
NULL

NULL is a symbolic constant defined in
stdio.h

#define NULL 0

This is a special pointer value, defined by C
language

The number 0 is not the same as the address 0 !
They are different types !

It is guaranteed that a pointer is NOT NULL,
unless programmer sets it to NULL
A side note on NULL

NULL is not the number 0

NULL is not necessarily the address 0

NULL is just a special value for pointers told to us by C
language.

Very often we need special values for a certain type

E.g. the value '\0' for a character is universally taken to be a special
value indicating end of a character sequence in an array

INT_MAX , INT_MIN are values #defined in limits.h for integers

These type of values are used in programs to indicate either an
unused variable or empty variable or error value on that variable

Int i = NULL; char c = NULL; works

Why do you want to do it? Instead of saying int i = 0; char c = 0;
Dangling Pointers

Total 3 Possibilities for a pointer value

Points to memory owned by program
 Local variables, Global Variables, Malloced Memory

= NULL

Dangling
 Some texts differentiate between ‘wild’ (uninitialized) and ‘dangling’
pointers

Dereferencing (that is * ) a dangling (or NULL) pointer
is NOT to be done

Even if you find that dereferencing a dangling pointer “does
not create problem” in your code, it is still not to be done!

Result: The OS punishes your code by terminating it, and
Dangling Pointers

A pointer is dangling when declared & not
initialized

A pointer becomes non dangling, when
assigned to a “good” memory location like local
variables, global variables, malloc-ed memory

A pointer can become dangling again due to

Mistakes in pointer arithmetic

Mistakes in manipulation of dynamically allocated
memory, e.g. Linked list pointers

On deallocation of malloced memory using free()
int main() {
        int *p, i, j, *q;// p,q are dangling
        
}

p i q j

Dangling Pointers Example: 1


int main() {
        int *p, i, j, *q;// p,q are dangling
        p = &i; // p not dangling
        
}

p i q j

Dangling Pointers Example: 1


int main() {
        int *p, i, j, *q;// p,q are dangling
        p = &i; // p not dangling
        i = 10; // *p = 10
       
}

p I = 10 q j

Dangling Pointers Example: 1


int main() {
        int *p, i, j, *q;// p,q are dangling
        p = &i; // p not dangling
        i = 10; // *p = 10
        q = &j; // q not dangling
       
}

p I = 10 q j

Dangling Pointers Example: 1


int main() {
        int *p, i, j, *q;// p,q are dangling
        p = &i; // p not dangling
        i = 10; // *p = 10
        q = &j; // q not dangling
        q = (int *)malloc(sizeof(int) * 4); 
// q not dangling
        
}

p I = 10 q j

Dangling Pointers Example: 1


int main() {
        int *p, i, j, *q;// p,q are dangling
        p = &i; // p not dangling
        i = 10; // *p = 10
        q = &j; // q not dangling
        q = (int *)malloc(sizeof(int) * 4); 
// q not dangling
        q = q + 3; // q not dangling
       }

p I = 10 q j

Dangling Pointers Example: 1


int main() {
        int *p, i, j, *q;// p,q are dangling
        p = &i; // p not dangling
        i = 10; // *p = 10
        q = &j; // q not dangling
        q = (int *)malloc(sizeof(int) * 4); 
// q not dangling
        q = q + 3; // q not dangling
        q = q + 1; // q IS dangling now

p I = 10 q j

Dangling Pointers Example: 1


int main() {
        int *p, i, j, *q;// p,q are dangling
        p = &i; // p not dangling
        i = 10; // *p = 10
        q = &j; // q not dangling
        q = (int *)malloc(sizeof(int) * 4); 
// q not dangling
        q = q + 3; // q not dangling
        q = q + 1; // q IS dangling now
        p = q ­ 6; // p IS also dangling
}

p I = 10 q j

Dangling Pointers Example: 1


typedef struct node { p q
int val;
struct node *next;
}node;
int main() { a
Val
node *p, *q;
node a;
} next

Dangling Pointers Example: 2


typedef struct node { p q
int val;
struct node *next;
}node;
int main() { a
Val =20
node *p, *q;
node a;
a.val = 20; next
}

Dangling Pointers Example: 2


typedef struct node { p q
int val;
struct node *next;
}node;
int main() { a
Val =20
node *p, *q;
node a;
a.val = 20; next
p = &a;

Dangling Pointers Example: 2


typedef struct node { p q
int val;
struct node *next;
}node;
int main() { a
node *p, *q; Val =20
node a;
a.val = 20; next
p = &a;
a.next = (node *)malloc(sizeof(node));
Val
}

next

Dangling Pointers Example: 2


typedef struct node { p q
int val;
struct node *next;
}node;
int main() { a
node *p, *q; Val =20
node a;
a.val = 20; next
p = &a;
a.next = (node *)malloc(sizeof(node));
a.next->val = 50; Val=50

}
next

Dangling Pointers Example: 2


typedef struct node { p q
int val;
struct node *next;
}node;
int main() { a
node *p, *q; Val =20
node a;
a.val = 20; next
p = &a;
a.next = (node *)malloc(sizeof(node));
a.next->val = 50; Val=50
q = a.next;

} next

Dangling Pointers Example: 2


typedef struct node { p q
int val;
struct node *next;
}node;
int main() { a
node *p, *q; Val =20
node a;
a.val = 20; next
p = &a;
a.next = (node *)malloc(sizeof(node));
a.next->val = 50;
q = a.next;
free(a.next);

Dangling Pointers Example: 2


typedef struct node { p q
int val;
struct node *next;
}node;
int main() { a
node *p, *q; Val =20
node a;
a.val = 20; next
p = &a;
a.next = (node *)malloc(sizeof(node));
a.next->val = 50;
q = a.next;
free(a.next);
q->val = 100; // segfault

Dangling Pointers Example: 2


Remember

Dereferencing a dangling pointer is a cause of
segmentation fault

Dereferencing can occur using * or [ ]

Having a dangling pointer does not cause
segmentation fault

Dereferencing it causes

Having dangling pointers is not wrong, but why
have them?
Garbage p was pointing to malloced memory
In the end p points to i
Memory How to access the malloced memory
now?
int *p, i; No way! It's Lost!
It became “garbage memory”
p = malloc Problems like this result in memory
waste
(sizeof(int) * 4);
Also called as Memory leak
p = &i; Solution
Keep another point pointing to
i malloced memory
p = malloc(sizeof(int) * 4);
q = p;
p p = &i;
0 1 2 3 Malloced memory is available through
pointer q
Segmentation Fault
What is segmentation fault?

A program accessing an “illegal” memory location

Is punished by the operating system for doing so

The program gets “terminated” (killed)

“segmentation” comes from “memory management”
concepts in operating systems

It is ALWAYS a fault of the programmer!

Beware

Bad compilers may generate machine code which hide
some memory violations

If a programmer does illegal memory access, segmentation
fault may not occur sometimes!
 OS may “forgive” your program :-;
Some Typical Reasons for Seg-fault
Deferencing Dangling Pointers

Array Index Violation

Incorrect Function Arguments


Dangling Pointer Dereference: Some
examples
int *p, i; int *f(int *p) {

*p = 20; int x = *p + 2;
return &x;
int *p, i;
}
p = malloc( int main() {
sizeof(int)*2); int i, *q;
scanf(“%d”, &i);
free(p);
q = f(&i);
p[2] = 20; *q = 20;
int *p, i; }
Don't return address of a local variable! The
p = &i; variable disappears after function call, and the
returned address is a dangling pointer.
p[1] = 20;
Array Index Violation
Valid indices for an array of size n: 0 .. n-1
Accessing any index <=-1 or >=n may result in
seg-fault (it may not result in some cases, but it
is STILL WRONG to do it!)
#include <stdio.h>
int main() {
Try this code: int a[16], i = 0;
At what while(1) {
value of i a[i] = i;
does the printf("%d\n", a[i]);
code seg- i++;
fault? Try }
2-3 times. }
Reason: OS may allocate more than 16-integer size
memory for the program. So the seg fault may not occur
at index 16 or slightely higher indices also. Rule: wrong to
access index >=n
Functions: Pointer Arguments

Rule: When you want a function to change ACTUAL
arguments

Function takes pointer arguments

Call passes address of “actual argument”

Example: Swap function (correct code)
void swap(int *a, int *b ) {
int temp;
temp = *a; 
*a = * b;
*b = temp;
}
int main() {
      int m = 20, n = 30;
      swap(&m, &n); 
}
Incorrect Pointer Aruments -
Segfault
int i;
scanf(“%d”, i);
Scanf tries to do something like
*i = value // segfault here
Segfault occurs in scanf, although the reason is call to
scanf

Note that this is basically a dangling pointer
dereference which took place inside scanf()
Guidelines to avoid segfaults

Always initialize pointers to NULL

This will not avoid segfaults, but may lead to early
detection of a problem

While accessing arrays, make sure that array
index is valid

Never return address of local variable of a
function

DO NOT IGNORE compiler's warnings

Compilers often warn about potential dangling
references, type conversions which have a potential
for segfaults

Make sure that you rewrite code to remove all
warnings

Use “-Wall” option with gcc. > cc -Wall program.c -o
program
Let’s draw diagrams for some programs using
pointers, malloc, free, ...

You might also like