Pointers in C

Download as pdf or txt
Download as pdf or txt
You are on page 1of 52

Pointers in C

What is a pointer

•In a generic sense, a “pointer” is anything that


tells us where something can be found.
– Addresses in the phone book
– URLs for webpages
– Road signs
•Computers store data in memory slots
•Each slot has an unique address
•Variables store their values like this:
Addr Content Addr Content Addr Content Addr Content
1000 i: 37 1001 j: 46 1002 k: 58 1003 m: 74
1004 a[0]: ‘a’ 1005 a[1]: ‘b’ 1006 a[2]: ‘c’ 1007 a[3]: ‘\0’
1008 ptr: 1001 1009 … 1010 1011
Pointer and Memory

•Altering the value of a variable is indeed


changing the content of the memory
 e.g. i = 40; a[2] = ‘z’;

Addr Content Addr Content Addr Content Addr Content


1000 i: 40 1001 j: 46 1002 k: 58 1003 m: 74
1004 a[0]: ‘a’ 1005 a[1]: ‘b’ 1006 a[2]: ‘z’ 1007 a[3]: ‘\0’
1008 ptr: 1001 1009 … 1010 1011

 Compare from the previous slide table the changes and


differences.
Pointer Caution

• They are a powerful low-level device.


• Undisciplined use can be confusing and
thus the source of subtle, hard-to-find
bugs. Following problems may occur
– Program crashes
– Memory leaks
– Unpredictable results
Addressing Concept

•Pointer stores the address of another entity


•It refers to a memory location

int i = 5;
int *ptr; /* declare a pointer variable */
ptr = &i; /* store address-of i to ptr */
printf(“*ptr = %d\n”, *ptr); /* refer to referee of ptr */

5
Why do we need Pointer?

•Simply because it’s there!


•It is used in some circumstances in C

Remember this?
scanf(“%d”, &i);

6
What actually ptr is?

•ptr is a variable storing an address


•ptr is NOT storing the actual value of i

Example:
ptr address of i
int i = 5;
int *ptr;
ptr = &i; i 5
printf(“i = %d\n”, i); Output:
printf(“*ptr = %d\n”, *ptr); i = 5 value of ptr
printf(“ptr = %p\n”, ptr); = address of
*ptr = 5
i in memory
ptr = effff5e0
Pointer Operations in C

•Creation
& variable Returns variable’s memory address

•Dereference
* pointer Returns contents stored at address

•Indirect assignment
* pointer = val Stores value at address

Also have...

•Assignment
pointer = ptr Stores pointer in another variable
Twin Operators
&: Address-of operator
 Get the address of an entity
• e.g. ptr = &j;

Addr Content Addr Content Addr Content Addr Content


1000 i: 40 1001 j: 33 1002 k: 58 1003 m: 74
1004 ptr: 1001 1005 1006 1007

9
Twin Operators
*: De-reference operator
 Refer to the content of the referee
• e.g. *ptr = 99;

Addr Content Addr Content Addr Content Addr Content


1000 i: 40 1001 j: 99 1002 k: 58 1003 m: 74
1004 ptr: 1001 1005 1006 1007

10
C Pointer Variables

To declare a pointer variable, we must do


two things
– Use the “*” (star) character to indicate that the
variable being defined is a pointer type.

– Indicate the type of variable to which the


pointer will point (the pointee). This is
necessary because C provides operations on
pointers (e.g., *, ++, etc) whose meaning
depends on the type of the pointee.

• General declaration of a pointer


data type *nameOfPointer;
Pointer Declaration

The declaration
int *intPtr;
defines the variable intPtr to be a pointer to a
variable of type int. intPtr will contain the
memory address of some int variable or int array.
Read this declaration as
– “intPtr is a pointer to an int”, or equivalently
– “*intPtr is an int”

Caution -- Be careful when defining multiple


variables on the same line. In this definition
int *intPtr, intPtr2;

intPtr is a pointer to an int, but intPtr2 is not!


Pointer Operators
Two primary operators used with pointers are
* (star) and & (ampersand)
– The * operator is used to define pointer variables and
to deference a pointer. “Dereferencing” a pointer
means to use the value of the pointee.

– The & operator gives the address of a variable.


observe the use of & in scanf( )
Pointer Examples
int x = 1, y = 2, z[10];
int *ip; /* ip is a pointer to an int */

ip = &x; /* ip points to (contains the memory address of) x */


y = *ip; /* y is now 1, indirectly copied from x using ip */
*ip = 0; /* x is now 0 */
ip = &z[5]; /* ip now points to z[5] */

If ip points to x, then *ip can be used anywhere x can be used so in this


example *ip = *ip + 10; and x = x + 10; are equivalent

The * and & operators bind more tightly than arithmetic operators so
y = *ip + 1; takes the value of the variable to which ip points, adds 1 and
assigns it to y

Similarly, the statements *ip += 1; and ++*ip; and (*ip)++; all increment
the variable to which ip points. (Note that the parenthesis are necessary
in the last statement; without them, the expression would increment ip
rather than what it points to since operators like * and ++ associate from
right to left.)
Pointer and Variable types
Note: The type of a pointer and its pointee must match

int a = 42;
int *ip;
double d = 6.34;
double *dp;

ip = &a; /* correct -- as types match */


dp = &d; /* ok */
ip = &d; /*compiler error– as data type mismatch */
dp = &a; /*compiler error– as dats type mismatch */
More Pointer example
• Use ampersand ( & ) to obtain the address of the pointee

• Use star ( * ) to get / change the value of the pointee

• Use %p to print the value of a pointer with printf( )

What is the output from below codes?


int a = 1, *ptr1;

/* show value and address of a and value of the pointer */


ptr1 = &a ;
printf("a = %d, &a = %p, ptr1 = %p, *ptr1 = %d\n",
a, &a, ptr1, *ptr1) ;

/* change the value of a by dereferencing ptr1 and then print


again */
*ptr1 = 35 ;
printf(“a = %d, &a = %p, ptr1 = %p, *ptr1 = %d\n", a,
&a, ptr1, *ptr1) ;
NULL

• NULL is a special value which may be assigned to


a pointer
• NULL indicates that this pointer does not point to
any variable (there is no pointee)
• Often used when pointers are declared
int *pInt = NULL;
• Often used as the return type of functions that
return a pointer to indicate function failure
int *myPtr;
myPtr = myFunction( );
if (myPtr == NULL){
/* something bad happened */
}
• Dereferencing a pointer whose value is NULL will
result in program termination.
Exercise
• What is the output from this code?

void F (int a, int *b) Output


{ 4, 4
a = 7 ;
*b = a ; 3, 7
b = &a ;
*b = 4 ;
printf("%d, %d\n", a, *b) ;
}
/* here function is used, my be skipped till next lecture on
functions. However execute and observe the output*/
int main()
{
int m = 3, n = 5;
F(m, &n) ;
printf("%d, %d\n", m, n) ;
return 0;
}
Example: Using Pointers

int i1;
int i2; Address: Content
int *ptr1; 0x1014 …
int *ptr2;
0x1010

i1 = 1; 0x100C …
i2 = 2;
0x1008

ptr1 = &i1; 0x1004


ptr2 = ptr1; 0x1000

*ptr1 = 3;
i2 = *ptr2;
Example: Using Pointers

int i1;
int i2;
int *ptr1; 0x1014 …
int *ptr2;
0x1010 ptr2:
i1 = 1; 0x100C …
i2 = 2;
0x1008 ptr1:

ptr1 = &i1; 0x1004 i2:


ptr2 = ptr1; 0x1000 i1: 1

*ptr1 = 3;
i2 = *ptr2;
Example: Using Pointers

int i1;
int i2;
int *ptr1; 0x1014 …
int *ptr2;
0x1010 ptr2:
i1 = 1; 0x100C …
i2 = 2;
0x1008 ptr1:

ptr1 = &i1; 0x1004 i2: 2


ptr2 = ptr1; 0x1000 i1:

*ptr1 = 3;
i2 = *ptr2;
Example: Using Pointers

int i1;
int i2;
int *ptr1; 0x1014 …
int *ptr2;
0x1010 ptr2:
i1 = 1; 0x100C …
i2 = 2;
0x1008 ptr1: 0x1000

ptr1 = &i1; 0x1004 i2: 2


ptr2 = ptr1; 0x1000 i1: 1

*ptr1 = 3;
i2 = *ptr2;
Example: Using Pointers

int i1;
int i2;
int *ptr1; 0x1014 …
int *ptr2;
0x1010 ptr2: 0x1000
i1 = 1; 0x100C …
i2 = 2;
0x1008 ptr1: 0x1000

ptr1 = &i1; 0x1004 i2: 2


ptr2 = ptr1; 0x1000 i1: 1

*ptr1 = 3;
i2 = *ptr2;
Example: Using Pointers

int i1;
int i2;
int *ptr1; 0x1014 …
int *ptr2;
0x1010 ptr2: 0x1000
i1 = 1; 0x100C …
i2 = 2;
0x1008 ptr1: 0x1000

ptr1 = &i1; 0x1004 i2: 2


ptr2 = ptr1; 0x1000 i1: 13
*ptr1 = 3;
i2 = *ptr2;
Example: Using Pointers

int i1;
int i2;
int *ptr1; 0x1014 …
int *ptr2;
0x1010 ptr2: 0x1000
i1 = 1; 0x100C …
i2 = 2;
0x1008 ptr1: 0x1000

ptr1 = &i1; 0x1004 i2: 2 3


ptr2 = ptr1; 0x1000 i1: 3

*ptr1 = 3;
i2 = *ptr2;
Using Pointers (cont.)
int int1 = 1036; /* some data to point to */
int int2 = 8;

int *int_ptr1 = &int1; /* get addresses of data */


int *int_ptr2 = &int2;

*int_ptr1 = int_ptr2;

*int_ptr1 = int2;

What happens?

Type check warning: int_ptr2 is not an int

int1 becomes 8
Using Pointers (cont.)
int int1 = 1036; /* some data to point to */
int int2 = 8;

int *int_ptr1 = &int1; /* get addresses of data */


int *int_ptr2 = &int2;

int_ptr1 = *int_ptr2;

int_ptr1 = int_ptr2;

What happens?

Type check warning: *int_ptr2 is not an int *

Changes int_ptr1 – doesn’t change int1


Pointer Arithmetic
pointer + number pointer – number

E.g., pointer + 1 adds 1 something to a pointer

char *p; int *p;


char a; int a;
char b; int b;

p = &a; p = &a;
p += 1; In each, p now points to b p += 1;
(Assuming compiler doesn’t
reorder variables in memory)

Adds 1*sizeof(char) to Adds 1*sizeof(int) to


the memory address the memory address

Pointer arithmetic should be used cautiously


The Simplest Pointer in C

Special constant pointer NULL


 Points to no data
 Dereferencing illegal – causes segmentation fault

 To define, include <stdlib.h> or <stdio.h>


Generic Pointers

void *: a “pointer to anything”


type cast: tells the compiler to
void *p; “change” an object’s type (for type
int i; checking purposes – does not modify
char c; the object in any way)
p = &i;
p = &c; Dangerous! Sometimes necessary…
putchar(*(char *)p);

Lose all information about what type of thing


is pointed to
 Reduces effectiveness of compiler’s type-checking
 Can’t use pointer arithmetic
Pass-by-Reference

void
set_x_and_y(int *x,
int *y)
{ Content
*x = 1001;
*y = 1002; a 1
}
b 2
void
f(void)
{
int a = 1;
int b = 2;
set_x_and_y(&a,&b);
}
Pass-by-Reference

void
set_x_and_y(int *x,
int *y)
{ Content
*x = 1001;
*y = 1002; a 1
}
b 2
void
f(void)
{ x
int a = 1;
int b = 2; y
set_x_and_y(&a,&b);
}
Pass-by-Reference

void
set_x_and_y(int *x,
int *y)
{
*x = 1001;
*y = 1002; a 1001
1
}
b 1002
2
void
f(void)
{ x
int a = 1;
int b = 2; y
set_x_and_y(&a,&b);
}
Next Example: Pass by Reference

•Modify behaviour in argument passing

void f(int j) void f(int *ptr)


{ {
j = 5; *ptr = 5;
} }
void g() void g()
{ {
int i = 3; int i = 3;
f(i); f(&i);
} i=? } i=?
Another Example: Pass by Reference

•Modify behaviour in argument passing

void f(int j) void f(int *ptr)


{ {
j = 5; *ptr = 5;
} }
void g() void g()
{ {
int i = 3; int i = 3;
f(i); f(&i);
} i=3
? } i=5
?
An Illustration

int i = 5, j = 10;
int *ptr;
int **pptr;
ptr = &i;
pptr = &ptr;
*ptr = 3; Data Table
**pptr = 7; Name Type Description Value
ptr = &j; i int integer variable 5
**pptr = 9; j int integer variable 10
*pptr = &i;
*ptr = -2;
An Illustration

int i = 5, j = 10;
int *ptr; /* declare a pointer-to-integer variable */
int **pptr;
ptr = &i;
pptr = &ptr;
*ptr = 3; Data Table
**pptr = 7; Name Type Description Value
ptr = &j; i int integer variable 5
**pptr = 9; j int integer variable 10
*pptr = &i; ptr int * integer pointer variable
*ptr = -2;
An Illustration

int i = 5, j = 10;
int *ptr;
int **pptr; /* declare a pointer-to-pointer-to-integer variable */
ptr = &i;
pptr = &ptr;
*ptr = 3; Data Table
**pptr = 7; Name Type Description Value
ptr = &j; i int integer variable 5
**pptr = 9; j int integer variable 10
*pptr = &i; ptr int * integer pointer variable
*ptr = -2;
pptr int ** integer pointer pointer variable

Double
Indirection
An Illustration

int i = 5, j = 10;
int *ptr;
int **pptr;
ptr = &i; /* store address-of i to ptr */
pptr = &ptr;
*ptr = 3; Data Table
**pptr = 7; Name Type Description Value
ptr = &j; i int integer variable 5
**pptr = 9; j int integer variable 10
*pptr = &i; ptr int * integer pointer variable address of i
*ptr = -2;
pptr int ** integer pointer pointer variable
*ptr int de-reference of ptr 5
An Illustration

int i = 5, j = 10;
int *ptr;
int **pptr;
ptr = &i;
pptr = &ptr; /* store address-of ptr to pptr */
*ptr = 3; Data Table
**pptr = 7; Name Type Description Value
ptr = &j; i int integer variable 5
**pptr = 9; j int integer variable 10
*pptr = &i; ptr int * integer pointer variable address of i
*ptr = -2; pptr int ** integer pointer pointer variable address of ptr
*pptr int * de-reference of pptr value of ptr
(address of i)
An Illustration

int i = 5, j = 10;
int *ptr;
int **pptr;
ptr = &i; Data Table
pptr = &ptr; Name Type Description Value
*ptr = 3; i int integer variable 3
**pptr = 7; j int integer variable 10
ptr = &j;
ptr int * integer pointer variable address of i
**pptr = 9;
pptr int ** integer pointer pointer variable address of ptr
*pptr = &i;
*ptr int de-reference of ptr 3
*ptr = -2;
An Illustration

int i = 5, j = 10;
int *ptr;
int **pptr;
ptr = &i;
pptr = &ptr;
*ptr = 3; Data Table
**pptr = 7; Name Type Description Value
ptr = &j; i int integer variable 7
**pptr = 9; j int integer variable 10
*pptr = &i; ptr int * integer pointer variable address of i
*ptr = -2;
pptr int ** integer pointer pointer variable address of ptr
**pptr int de-reference of de-reference of 7
pptr
An Illustration

int i = 5, j = 10;
int *ptr;
int **pptr;
ptr = &i;
pptr = &ptr;
*ptr = 3; Data Table
**pptr = 7; Name Type Description Value
ptr = &j; i int integer variable 7
**pptr = 9; j int integer variable 10
*pptr = &i; ptr int * integer pointer variable address of j
*ptr = -2;
pptr int ** integer pointer pointer variable address of ptr
*ptr int de-reference of ptr 10
An Illustration

int i = 5, j = 10;
int *ptr;
int **pptr;
ptr = &i;
pptr = &ptr;
*ptr = 3; Data Table
**pptr = 7; Name Type Description Value
ptr = &j; i int integer variable 7
**pptr = 9; j int integer variable 9
*pptr = &i; ptr int * integer pointer variable address of j
*ptr = -2;
pptr int ** integer pointer pointer variable address of ptr
**pptr int de-reference of de-reference of 9
pptr
An Illustration

int i = 5, j = 10;
int *ptr;
int **pptr;
ptr = &i;
pptr = &ptr;
*ptr = 3; Data Table
**pptr = 7; Name Type Description Value
ptr = &j; i int integer variable 7
**pptr = 9; j int integer variable 9
*pptr = &i; ptr int * integer pointer variable address of i
*ptr = -2;
pptr int ** integer pointer pointer variable address of ptr
*pptr int * de-reference of pptr value of ptr
(address of i)
An Illustration

int i = 5, j = 10;
int *ptr;
int **pptr;
ptr = &i;
pptr = &ptr;
*ptr = 3; Data Table
**pptr = 7; Name Type Description Value
ptr = &j; i int integer variable -2
**pptr = 9; j int integer variable 9
*pptr = &i; ptr int * integer pointer variable address of i
*ptr = -2;
pptr int ** integer pointer pointer variable address of ptr
*ptr int de-reference of ptr -2
Addresses and Pointers

•Since each variable is a location,


 we can refer to its address
 but cannot change it
•C syntax for addresses:

Given a declaration of the form


T v;
&v refers to the address of v.
Addresses and Pointers (in C)

Given declarations
int v;
int *p;
p = &v is a valid assignment statement.

v 5

What is the effect of the following?


v = 5;
Addresses and Pointers (in C)

Now, what is the effect of *p++ ?


v
5
* Binds tighter than ++,
so this is same as
p
*p = *p + 1;

*p (i.e., contents of p) is 5;

v 6
And it is changed to 6;
p
So v is also 6
Arrays and Pointers (in C)

Given Ai
int Ai[100]; // array of 100 ints
Ai is the starting address of the array.

So, Ai[5] is same as *(Ai+5)

Observe that Ai+5 is “address arithmetic”:


address Ai is added to int 5
to obtain an address.
Arrays and Pointers (in C)

Given Ai
int Ai[100];
int *pi;
pi

the following are valid:

pi = Ai

pi = Ai +2

pi – Ai which will evaluate to 2


Arrays and Pointers
Point to note: Passing arrays:
Array name  a pointer to the Must explicitly
Really int *array pass the size
initial (0th) array element
int
foo(int array[],
a[i]  *(a+i) unsigned int size)
{
… array[size - 1] …
An array is passed to a function
}
as a pointer
 The array size is lost!
int
main(void)
Usually bad style to interchange {
arrays and pointers int a[10], b[5];
 Avoid pointer arithmetic! … foo(a, 10)… foo(b, 5) …
}

You might also like