Vous êtes sur la page 1sur 41

Arrays and Pointers

R. Inkulu
http://www.iitg.ac.in/rinkulu/

(Arrays and Pointers)

1 / 41

Motivation with Example usage

int func(void) {
int states[5], i; //defining
for (i=0; i<5; i++)
states[i]=i; //initializing
for (i=0; i<5; i++)
printf("%d", states[i]); //accessing the value
return 0;
}
array is used in storing multiple objects of same type
objects in the array are indexed starting from 0
array scope is limited to the function in which it is defined

(Arrays and Pointers)

2 / 41

sizeof() operator

int func(void) {
int a[5];
printf("%d, %d, %d",
sizeof(int), sizeof(a[3]), sizeof(a));
//outputs 4, 4, 20
}
sizeof(object or type name) is a compile-time unary operator to get

size of an object or a type in number of bytes

(Arrays and Pointers)

3 / 41

Two-dimensional arrays
int func(void) {
int a[2][3], i, j;

//defining

for (i=0; i<2; i++)


for (j=0; j<3; j++)
a[i][j] = i+j; //initializing
for (i=0; i<2; i++)
for (j=0; j<3; j++)
printf("%d, ", a[i][j]);

//accessing the value

printf("%d, %d, %d", sizeof(a),


sizeof(a[1]), sizeof(a[1][2]));
//prints 24, 12, 4
return 0;
}
(Arrays and Pointers)

4 / 41

Three-dimensional arrays
int func(void) {
int a[2][3][4], i, j, k;

//defining

for (i=0; i<2; i++)


for (j=0; j<3; j++)
for (k=0; k<4; k++)
a[i][j][k] = i+j; //initializing
for (i=0; i<2; i++)
for (j=0; j<3; j++)
for (k=0; k<4; k++)
printf("%d, ", a[i][j][k]); //accessing
printf("%d, %d, %d, %d", sizeof(a), sizeof(a[1]),
sizeof(a[0][2]), sizeof(a[1][0][2]));
//prints ?
return 0;
}
(Arrays and Pointers)

5 / 41

Address vs Value

int a, b[2];
a = 10;
printf("%d, %p", a, &a);
//prints 10, 0xbfeac818
b[0] = 22; b[1] = 24;
printf("%d, %p, %d, %p", b[0], &b[0], b[1], &b[1]);
//prints 22, 0xbfeac820, 24, 0xbfeac824
memory for arrays is allocated contiguously

(Arrays and Pointers)

6 / 41

Arrays are stored in row-major order

int c[2][3], i, j;
for (i=0; i<2; i++)
for (j=0; j<3; j++) {
printf("%p, ", &c[i][j]);
}
//prints 0xbfd8f650, 0xbfd8f654, 0xbfd8f658,
//0xbfd8f65c, 0xbfd8f660, 0xbfd8f664,
contiguous memory is allocated for the first row, immediately

thereafter for the second row, etc.,

(Arrays and Pointers)

7 / 41

Arrays are stored in row-major order (cont)

int a[2][5][3], i, j, k;
for (i=0; i<2; i++)
for (j=0; j<5; j++)
for (k=0; k<3; k++)
printf("%p, ", &a[i][j][k]);
//observed: difference between any two successive
//addresses printed is four

(Arrays and Pointers)

8 / 41

Initializing arrays while defining

int a[2][5] = {
{0, 11, 22, 33, 44},
{1, 12, 23, 34, 45}
};
for (int i=0; i<2; i++)
for (int j=0; j<5; j++)
printf("%d, ", a[i][j]);
//prints 0, 11, 22, 33, 44, 1, 12, 23, 34, 45,

(Arrays and Pointers)

9 / 41

Introducing pointers
int x = 1, y = 2, z[2];
int *p = NULL;
//p is a pointer to int, initialized with NULL
p = &x;
y = *p;
*p = 0;
p = &z[1];
*p = 5;
*p = *p + 5;

//p points to x
//y is 1
//x is now 0
//p points to z[1]
//z[1] is 5
//z[1] is 10

pointer is jut a variable that saves the address of a memory

location that contains a specific type


when p points to a typeA, p is termed as a pointer to typeA
dereferencing pointer p is denoted with p
(Arrays and Pointers)

10 / 41

Incrementing a pointer
p

z[0]

z[1]

z[2]

z[3]

int z[4];
int *p = &z[0];
*(p+1) = 7;
p += 2;
*p = 5;
*p = *p + 3;
*p += 2;
++(*p);
int *q = p;
*q = 89;

z[0]

z[1]

z[2]

z[3]

//*(p+(1*sizeof(int))) = 7
//i.e., z[1] has 7
//p = p + (2*sizeof(int))
//z[2] has 5
//z[2] has 8
//z[2] has 10
//z[2] has 11
//q also points to z[2]
//z[2] has 89

when p is a pointer to typeA, expression p + i increments p by

i sizeof (typeA)

(Arrays and Pointers)

11 / 41

Ways to access an array


z+0
&z[0]
z[0]

z+2
&z[2]
z[1]
&z[1]
z+1

z[2]

p[0]
p[1]
*(p+0) *(p+1)

z[3]

p+0

&z[3]
z+3

p[2]
*(p+2)

p[3]
*(p+3)

&p[0]

p+1

&p[2]

&p[1]

p+2
&p[3]

p+3

double z[4], *p = z;
...
z[3] = 10;
...
ex. compiler replaces z[i] with (z + (i sizeof (double)))

same is true in case of multi-dimensional arrays


(Arrays and Pointers)

12 / 41

Array name is a pointer


float z[4];
printf("%d, %d, %d", sizeof(p),
sizeof(float), sizeof(*p)); //prints 4, 4, 4
printf("%p, %p, ", z, &z[0]);
//identical values are printed
printf("%p, %p, ", z+1, &z[0]+1);
//identical values (&z[1]) are printed
name of an array (z) is a synonym for the address of z[0];

further, &z[i] + j denotes the address of z[i + j]


however, statements like z = p and z++ are illegal as z is not a

variable
(Arrays and Pointers)

13 / 41

Array name is a pointer (cont)

float z[4];
float *p = &z[0], *q;
q = z;
printf("%p, %p, %p", z, p, q);
//identical values are printed
*(z+2) = 20;
q[3] = 52;
*(q+3) = 58;

(Arrays and Pointers)

//z[2] has 20
//z[3] has 52
//z[3] has 58

14 / 41

More on row-major order of arrays


z[0]
z[1]
z[2]
z[0], z[1], z[2] shown in figure do not occupy additional space

int z[3][4], i, j;
printf("%p, %p, %p, ", z[0], z[1], z[2]);
//prints addresses of three rows i.e, &z[i][0]
printf("%p, %p", &z[2]+1, &z[1]+2);
//prints identical values
printf("%p, %p", z[2]+1, &z[2][1]);
//prints identical values
two-dimensional array is an array of rows:

z[i] is the name of the array that comprises the ith row;
and, &z[i] is the address of the first row
(Arrays and Pointers)

15 / 41

Passing arguments to functions (review)


void func(int y) {
//y has 10
y = 15;
//y has 15
return;
}
int main(void) {
int x=10;
func(x);
printf("%d", x);
return 0;
}

//prints 10 !

function parameters are passed by value

(Arrays and Pointers)

16 / 41

Simulating pass by reference with pass by value

void func(int *y) {


//now the value of y is the address of x
//(x is a variable defined in main)
*y = 15;
}
int main(void) {
int x=10;
func(&x);
printf("%d", x);
}

(Arrays and Pointers)

//address of x is passed to func


//prints 15

17 / 41

Passing arguments to functions: swap func

void swap(int v, int w) {


int tmp = v;
v = w;
w = tmp;
}
int main(void) {
int x=10, y=15;
swap(x, y);
printf("%d, %d", x, y);
}

(Arrays and Pointers)

//prints 10, 15

18 / 41

Simulating pass by reference with pass by value:


swap func

void swap(int *v, int *w) {


int tmp = *v;
*v = *w;
*w = tmp;
}
int main(void) {
int x=10, y=15;
swap(&x, &y);
printf("%d, %d", x, y);
}

(Arrays and Pointers)

//prints 15, 10

19 / 41

Passing one-dimensional arrays to functions


int countNonZeros(int v[], int len) {
//v is a pointer to array x (defined in main), and
//len has 3
int count = 0, i;
for (i=0; i<len; i++)
if (v[i] != 0)
++count;
return count;
}
int main(void) {
int x[3] = {20, 30, 0};
int k = countNonZeros(x, 3);
printf("%d", k);
//prints 2
}
change of values of any element of v in countNonZeros gets reflects

in array x of main
(Arrays and Pointers)

20 / 41

Passing one-dimensional arrays to functions: an


alternative
int countNonZeros(int *v, int len) {
//v has &x[0], and len has 3
int count = 0, i;
for (i=0; i<len; i++)
if (v[i] != 0)
++count;
return count;
}
int main(void) {
int x[3] = {20, 30, 0};
int k = countNonZeros(x, 3);
printf("%d", k);
//prints 2
}
change of values of any element of v in countNonZeros gets reflects

in array x of main
(Arrays and Pointers)

21 / 41

Passing partial arrays to functions


int countNonZeros(int *v, int len) {
//v has &x[1], and len has 2
int count = 0, i;
for (i=0; i<len; i++)
if (v[i] != 0)
//equivalently,
++count;
return count;
}

if (*(v+i) != 0)

int main(void) {
int x[3] = {20, 30, 0};
int k = countNonZeros(&x[1], 2);
printf("%d", k);
//prints 1
}

(Arrays and Pointers)

22 / 41

Passing two-dimensional arrays to functions


void func(double b[][4], int numRows, int numCols)
//equivalently, formal parameter can be b[3][4]
{
...
b[2][3] = 78;
//lvalue is (&b[0][0]+(2*4+2)*sizeof(double))
... }
int main(void) {
double a[3][4];
func(a, 3, 4);
return 0; }
formal parameter need to include the number of columns; the

number of rows are irrelevant: since what is passed is, a pointer to


an array of rows, where each row is an array of [4] integers (ex. to
access a particular element, compiler need to be able to calculate
the offset from the beginning of array b)
(Arrays and Pointers)

23 / 41

Passing multi-dimensional arrays to functions:


an alternative

Homework!

(Arrays and Pointers)

24 / 41

Dynamic Memory
p

double *p = (double *) malloc(count*sizeof(double));


//allocates count*sizeof(double) bytes contiguously;
//p has the address of first byte of those allocated
int i;
for (i=0; i<count; i++) {
p[i] = i*10;
printf("%lf, ", p[i]);
}
//prints 0.000000, 10.000000, 20.000000, 30.000000,
free(p);
//frees contigous memory referred by p
useful when the number of objects to be allocated is not known at

compile-time
system maintains a table of dynamically allocated memory blocks

and their corresp. address ranges


(Arrays and Pointers)

25 / 41

Dynamic vs non-dynamic memory allocation


z

z[0]

z[1]

z[2]

z[3]

double z[4]; //z is from stack


double *p = (double*) malloc(count*sizeof(double));
//p is from stack, and memory block is from heap
...
free(p);
memory for auto variables (including arrays) comes from stack,

whereas the dynamic memory comes from heap


of the process address space
(Arrays and Pointers)

26 / 41

Pointer arithmatic
at (1):
z[0]

double *p = (double*)
malloc(count*sizeof(double));
double *q = NULL;
... //denotes irrelevant
//stmts
//im here (1)
p += 2;
...
q = p;
p
//im here (2)
...
free(p);

z[1]

z[2]

z[3]

NULL
q

at (2):

z[0]

z[1]

z[2]

z[3]

pointer arithmatic is same whether the memory referred by a

pointer is either from the stack or heap


(Arrays and Pointers)

27 / 41

malloc and free pairs


double *p=NULL; int *q = NULL;
...
p = (double*) malloc(countA*sizeof(double));
...
q = (int*) malloc(countB*sizeof(int));
...
free(p);
...
free(q);
in any program, malloc and free must exist as pairs
avoiding free call corresp. to any malloc call causes memory leak
malloc and free corresp. to a block of memory
not necessarily be invoked in the same function
not necessarily bracketed
(Arrays and Pointers)

28 / 41

Dynamic memory: advantages and


disadvantages
advantages:
useful when the number of objects to be allocated is not known at

compile-time
gives flexibility to allocate and deallocate memory based on the

need; careful user of this primitive can extract benefits


disadvantages:
slow due to free/allocated heap space maintainance involved
together with the defragmentation overhead
due to intermittent mallocs and frees
forgetting to deallocate memory causes memory leak

(Arrays and Pointers)

29 / 41

Non-dynamic memory: advantages and


disadvantages

advantages:
compiler will deallocate the memory automatically for all the

global, static, and local memory that it allocated: no memory leak


hassles
disadvantages:
Memory allocation and deallocation are not in the control of user

(Arrays and Pointers)

30 / 41

Avoid dangling pointers after freeing


at (1):
p

...
double *p = (double*)
malloc(count*sizeof(double));
at (2):
...
p
//im here (1)
free(p);
...
dangling
//im here (2)
at (3):
p = NULL;
p
//im here (3)
NULL
...
avoid dangling pointers by resetting pointer variable value after

the free
(Arrays and Pointers)

31 / 41

Returning pointers from functions


double *func(int count) {
double *p = NULL;
...
p = (double*) malloc(count*sizeof(double));
...
return p; }
void func1(int countA) {
int count; double *q = NULL;
...
q = func(count);
...
free(q);
//freeing heap memory allocated in func
...
return; }
heap memory referred by a pointer may require to be freed by the

caller (precise protocol need to be defined)


does not make sense to return the address of a local variable (ex.
array) from a function
(Arrays and Pointers)

32 / 41

Returning pointers from functions (cont)

void *malloc(size_t sizeInBytes) {


...
}
void func() returns nothing

whereas void* func() returns pointer to a block of memory (could


be NULL too) that can contain objects of any type

(Arrays and Pointers)

33 / 41

Few useful dynamic memory related functions


from stdlib.h
void *malloc(size t numBytes)
void free(void *p)
void *calloc(size t numObj, size t sizeOfAObject)
same as malloc but zeros the memory allocated

void *realloc(void *oldMem, size t numBytes)


resizes and where necessary relocates the block pointed by p; moves the
contents of *p to the new location; frees the old memory

void *memcpy(void *to, void *from, int numBytes);


cannot handle the overlap

void *memmove(void *to, void *from, int numBytes);


same as memcpy but handles the overlap

(Arrays and Pointers)

34 / 41

Array of pointers to varying sized arrays


*(buf[0]+2) a.k.a. buf[0][2]

buf
buf[0]
buf[1]
STACK

HEAP

double *buf[2];
buf[0] = (double*) malloc(countA*sizeof(double));
buf[1] = (double*) malloc(countB*sizeof(double));
printf("%d, %d ", sizeof(buf[0]), sizeof(buf));
//prints sizeof(ptr), 2*sizeof(ptr)
...
free(buf[1]);
free(buf[0]);
buf is an array[2] of pointers, each entry of which points to a block

of memory that contains objects of type double


(Arrays and Pointers)

35 / 41

Array of pointers to varying sized arrays (cont)


z

buf
buf[0]
buf[1]

STACK

STACK
*(buf[0]+2) or *(buf[1])

double z[4];
double *buf[2];
buf[0] = &z[0];
buf[1] = &z[2];
printf("%d, %d ", sizeof(buf[0]), sizeof(buf));
//prints 4, 8
...
//no need of free calls

(Arrays and Pointers)

36 / 41

Array of pointers to fixed size arrays


buf

*(buf+2)

*(buf+0)
*(buf+1)

STACK

(*(buf+1))[1]

HEAP

double (*buf)[2] =
(double (*)[2])malloc(count*sizeof(double [2]));
printf("%d, %d, %d, %d",
sizeof(double), sizeof(buf[0]),
sizeof(buf[2]), sizeof(buf));
//prints 8, 16, 16, 4 when count is 3
...
free(buf);
buf points to count number of array[2]s of doubles

in other words, buf[0] is the zeroth arry[2]; buf[1] is the first


array[2]; etc.,
(Arrays and Pointers)

37 / 41

Passing two-dimensional arrays to functions: an


alternative
void func(double (*b)[4])
//b points to contiguous sequence of double [4]s
{
...
}
int main(void) {
double a[3][4];
func(a);
}

//a is an array[3] of array[4]s

as mentioned in the past, formal parameter must include the

number of columns; number of rows are irrelevant

(Arrays and Pointers)

38 / 41

Passing multi-dimensional arrays to functions:


an alternative

Homework!

(Arrays and Pointers)

39 / 41

Memory layouts of various declarations (review)


(i) double a[2];
double *p = &a[0];
(ii) double *p = (double *) malloc(count*sizeof(double));
(iii) double a[2][3];
(iv) double *a[2];
a[0] = (double *) malloc(countA*sizeof(double));
a[1] = (double *) malloc(countB*sizeof(double));
(v) double *a[2], b[2][3];
a[1] = b[0];
(vi) double (*b)[2];
b = (double (*)[2]) malloc(count*sizeof(double [2]));
(vii) double (*b)[2], c[4][2];
b = c;
homework: using these notions, engineer few more declarations
(Arrays and Pointers)

40 / 41

to be continued ...

(Arrays and Pointers)

41 / 41

Vous aimerez peut-être aussi