Abhijit A.M.: Pointers in C
Abhijit A.M.: Pointers in C
Abhijit A.M.
[email protected]
&
*
=
+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:
> 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
p i q j
p i q j
p I = 10 q j
p I = 10 q j
p I = 10 q j
p I = 10 q j
p I = 10 q j
p I = 10 q j
next
}
next
} next
*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, ...