0% found this document useful (0 votes)
29 views57 pages

04B Right Left Rule 2D Arrays

The document discusses 2D arrays in C/C++, including: - The "right-left rule" for parsing complex C declarations involving arrays and pointers. - Three types of 2D arrays: fixed/static size, semi-dynamic, and fully-dynamic. - The memory organization and access of each 2D array type, including whether the size must be known at compile-time and efficiency of element access.

Uploaded by

moamin.shikha
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
29 views57 pages

04B Right Left Rule 2D Arrays

The document discusses 2D arrays in C/C++, including: - The "right-left rule" for parsing complex C declarations involving arrays and pointers. - Three types of 2D arrays: fixed/static size, semi-dynamic, and fully-dynamic. - The memory organization and access of each 2D array type, including whether the size must be known at compile-time and efficiency of element access.

Uploaded by

moamin.shikha
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 57

Right-left rule

2D arrays

Programming Workshop in C/C++


(67315)
Expected Learning Outcomes
0 Parsing complex data types using the right-left rule
Understand

0 Types of 2D-arrays and their memory organization


0 static (M x N)
0 semi-dynamic (M x n, m x N)
0 fully-dynamic (m x n)
0 Data type of 2D-array arguments (and beyond)
0 Memory organization of argv/argc

0 Declare 2D arrays correctly using the right-left rule


Apply

Allocate and de-allocate 2D arrays of different types


0 Pass 2D arrays as function arguments
0 Return 2D arrays safely
2
The Right-Left rule

Parsing complex C declarations

3
The Right-Left rule
int x; // an int

int *x; // a pointer to an int

int x[5]; // an array of 5 ints

int x[5][10]; // an arrays of 5 arrays of 10 ints

const int x[5][10]; // an arrays of 5 arrays of 10 const ints

int (*x)[5]; // ?
int *x[5]; // ?
int *const x[5]; // ??
const int *x[5]; // ??
int *(*x[5])[10][13]; // ???
4
The Right-Left rule
int (*x)[5]

1. Find the identifier

2. Look at the symbols to the right until hitting ‘)’


● () means this is a function

● [] means this is an array


3. Look at the symbols to the left until hitting ‘(‘

4. Go to step 2 until declaration is complete 5


The Right-Left rule

char (*arr)[5]; char* arr[5];

“arr is a pointer “arr is an array


to an array of 5 of 5 pointers to
chars” char”

https://cdecl.org/

6
The Right-Left rule
int x; // an int

int *x; // a pointer to an int

int x[5]; // an array of 5 ints

int x[5][10]; // an arrays of 5 arrays of 10 ints

const int x[5][10]; // an arrays of 5 arrays of 10 const ints

int (*x)[5]; // a pointer to an array of 5 ints

int *x[5]; // an array of 5 pointers to int

int *const x[5]; // an array of 5 const pointers to int

const int *x[5]; // an array


// Ano array
5 pointers to const to
of 5 pointers intan array
// of 10 arrays of 13 pointers to int
int *(*x[5])[10][13];
7
2D arrays

Array of pointers, pointers to arrays,


dynamic memory allocation

8
Types of 2D-arrays

I. Fixed size (”static”)


double arr[M][N]; // M x N
II. Semi-dynamic
double *arr[M]; // M x n
double (*arr)[N]; // m x N
III. Fully-dynamic
double **arr; // m x n

9
Similarities & Differences
I. Fixed size (”static”) Similarities
double arr[M][N]; // M x N
Uniform access to elements
II. Semi-dynamic regardless of 2D-array type:
double *arr[M]; // M x n
double (*arr)[N]; // m x N
arr[i][j]
III. Fully-dynamic
double **arr; // m x n Differences
Storage:
• continuous vs. fragmented
• stack vs. heap
Access efficiency:
• single vs. multiple operations
Variable type:
• pointer/array/both
10
I. Fixed-size (“static”) arrays:
int arr[M][N]
• Continuous memory

• Efficient: one memory operation to access an entry

But it is not always practical –


• Size must be known at compile time

sizeof(arr) = M * N * sizeof(*arr)
I. Fixed-size arrays: int arr[4][8]
STACK

arr[0][0] arr[0][1] arr[0][7]

arr[1][0]

How it is actually stored in memory:


arr[0][0] arr[1][0]

int A[M][N];
A[i][j] = 5; // equivalent to *(*(A+i)+j) = 5, or in other
row
words, go to memory address [&A[0] + (i*N + j) * sizeof(int)]
(in bytes) and write 5 into it
12
II-a. Semi-dynamic arrays: int *arr[M]
An array of M pointers to int

#define M 4 // # rows - compile-time


int *arr[M] = {0}; // stack memory for M pointers
for (int i = 0; i < M; i++)
{
arr[i] = malloc (sizeof (int) * n); // eg, n=8
if(arr[i] == NULL) { <… handle it …> }
}
STACK
arr HEAP
0X557
 a static array of
0X557 M pointers, each
0X28f
pointing to a
0X462
dynamic array of
length n
0X71c
II-a. Semi-dynamic arrays: int *arr[M]
An array of M pointers to int

STACK arr[0][0]
HEAP
0X557
arr

0X557 arr[1][0]
0X28f

0X28f
0X462

0X462

0X71c

0X71c

14
II-a. Semi-dynamic arrays: int *arr[M]
An array of M pointers to int

• Flexibility
size of each row might be different
• Complexity
row sizes must be traced correctly
• Less efficiency
Two memory operations to access an entry
• ~Practicality
Number of rows must be known at compile time
Don’t forget to free
One free() per m/calloc()
int *arr[M] = {0};
for (int i = 0; i < M; i++)
{
arr[i] = malloc (n * sizeof (int));
if (!arr[i]) { <… handle it …> }
}

for (int i = 0; i < 4; i++)
{
free (arr[i]);
}

16
Do not return M x n arrays!
We cannot pass ownership of a static array!

int *arr[M] = {0};


for (int i = 0; i < M; i++)
{
arr[i] = malloc (n * sizeof (int));
if (!arr[i]) { <… handle it …> }
}

// Never return static (fixed-length) arrays!
// Recall functions only return pointers
return arr; // returns &arr[0]

17
II-b. Semi-dynamic arrays: int (*arr)[N]
A pointer to an array of N ints

int m = 4; // # rows - run-time


#define N 8 // # columns - compile-time
int (*arr)[N] = // sizeof (*arr) == sizeof (int[N])
calloc (m, sizeof (*arr));
arr[1][6] = 10; // ?
return arr; // passing ownership OK! 

Stack Heap a contiguous


dynamic array
arr 0X557 0X557
arr[0] of m static
Pa[1]
arrays with N
arr[1] [6] elements each
arr[2] All in the heap
arr[3] a single pointer!
II-b. Semi-dynamic arrays: int (*arr)[N]
A pointer to an array of N ints

• Flexibility
size of each column might be different
• Complexity
column sizes must be traced correctly
• Less efficiency (vs. fixed-size array)
Two memory operations to access an entry
• ~Practicality
Number of columns must be known at compile time
III. A fully-dynamic array: int **arr
How do we allocate an m x n fully-dynamic array?

int **arr;

STACK
arr
.
.
.
.
.
.

20
III. A fully-dynamic array: int **arr
How do we allocate an m x n fully-dynamic array?

int **arr;
arr = malloc (m * sizeof(int*)); // eg, m=4
)

STACK
0X32f
HEAP
0X32f

.
.
.
.
.
.

21
III. A fully-dynamic array: int **arr
How do we allocate an m x n fully-dynamic array?
int **arr;
arr = malloc (m * sizeof(int*)); // m x sizeof(int*)
if (!arr) {… handle error …}
for (i = 0; i < m; i++)
{
arr[i] = malloc (n * sizeof (int)); // e.g. n=8
if (!arr[i]) {… handle error …}
}
STACK
0X32f
HEAP
0X557
.
. 0X28f
.
. 0X462
.
. 0X71c

22
III. A fully-dynamic array: int **arr
STACK HEAP
arr[0][0]
arr 0X32f 0X557
0X32f

0X557
arr[1][0]
. 0X28f

. 0X28f

.
0X462
.
0X462
.
.
. 0X71c
0X71c

23
III. A fully-dynamic array: int **arr

• Numbers of both rows and columns


may be unknown at compile-time

• Even less efficient than semi-


dynamic arrays: three memory
operations to access an entry
Reminder:
malloc() does not initialize
the cells with valid values

25
III. fully-dynamic arrays: int **arr;
int** arr;
arr = malloc(m*sizeof(*arr)); // m=4
if (!arr == NULL) {… handle error …}
for (i = 0; i < m; i++)
{
arr[i] = calloc(n, sizeof(**arr)); // n=8
if (!arr[i]) {… handle error …}
}
STACK
0X32f
HEAP
0X32f
0X557
0 0 0 0 0 0 0 0
.
0 0 0 0 0 0 0 0
. 0X28f
.
. 0X462 0 0 0 0 0 0 0 0
.
. 0X71c 0 0 0 0 0 0 0 0

26
Free!
• Don’t forget to free all the memory –
one free call per m/calloc call

e.g., for a full dynamically-allocated 2D-array:


for (i = 0; i < nrows; i++)
{
free (array[i]); // free each row
}
free (array); // free master ptr
array = NULL;

27
Revisiting argc/argv –
a special 2D-array

30
Passing arguments to a program with argc and argv – an array of arrays

argc
• stands for “argument count”
• contains the number of arguments passed
to the program
argv
• stands for “argument vector”
• array of strings
myprog 1 2 3 >

argc == 4 (program name is the first)


argv[0] --> "myprog"
argv[1] --> "1"
argv[2] --> "2"
argv[3] --> "3"
Passing arguments to a program
with argc and argv

int main (int argc, char *argv[])


{
for (int i=0; i < argc; i++)
{
printf("%s ", argv[i]);
}

// prints the first character of program name


printf ("%c", argv[0][0]);
}
Passing multi-dimensional
arrays to functions

33
Reminder – a 1D-array argument is always
converted to a pointer

int foo (int *p);


int foo (int a[]); // same
int foo (int a[NUM]); // same! (NUM ignored)

All declare the same interface:

In all cases, a pointer to int is being passed to


the function foo

34
 The first dimension in a 2D-array
argument is always converted to a pointer
Just use the right-left rule:
• If an argument is an array of <something>, it is converted to a pointer to
<something>.
• For example, int a[4][N] is an array of N-sized arrays
 it is passed as a pointer to N-sized arrays

int foo (int (*p)[N]); // a pointer to int[N]


int foo (int p[][N]); // a pointer to int[N]
int foo (int p[4][N]); // a pointer to int[N]

int goo (int **p); // a pointer to int*


int goo (int *p[]); // a pointer to int*
int goo (int *p[N]); // a pointer to int*

int hoo (int p[][]); // error – 2nd dimension onward


// must be specified
35
The first dimension in any k-dimensional array argument is converted to a pointer

// The following three declarations are equivalent:


void func(int (*x)[7][3]); // a ptr to int[7][3]
void func(int x[][7][3]); // a ptr to int[7][3]
void func(int x[5][7][3]); // a ptr to int[7][3]

What is the size of x, *x, **x, and ***x?


• sizeof(x)  sizeof(void*)
• sizeof(*x)  7 * 3 * sizeof(int)
• sizeof(**x)  3 * sizeof(int)
• sizeof(***x)  sizeof(int)

37
A 2D array
memory leak example

38
Example: a 3 x 2 fully-dynamic 2D-array
void create_memory_leak () {
int i, j;
int **m = malloc (sizeof (int*) * 3);
for (i = 0; i < 3; i++) {
m[i] = malloc (sizeof (int) * 2);
for (j = 0; j < 2; j++)
{
m[i][j] = i + j;
}
}
}
In the following example we will assume
sizeof (int) == sizeof (void*) == 4
39
Another memory leak example
stack heap int i, j;
int **m = malloc
999
20 24 28 32 36 (sizeof (int*)*3);
i 0 for (i = 0; i < 3; i++)
995 {
40 44 48 52 56
j 0 x x
m[i] = malloc
(sizeof (int)*2);
991
60 64 68 72 76 for (j = 0; j < 2; j++)
m x x x x x {
m[i][j] = i + j;
987
80 84 88 92 96 }
x }
983

40
Another memory leak example
stack heap int i, j;
m[0] m[1] m[2] int **m = malloc
999
20 24 28 32 36 (sizeof (int*)*3);
i 0 for (i = 0; i < 3; i++)
995 {
40 44 48 52 56
j 0 x x
m[i] = malloc
(sizeof (int)*2);
991
60 64 68 72 76 for (j = 0; j < 2; j++)
m 20 x x x x x {
m[i][j] = i + j;
987
80 84 88 92 96 }
x }
983

41
Another memory leak example
stack heap int i, j;
m[0] m[1] m[2] int **m = malloc
999
20 24 28 32 36 (sizeof (int*)*3);
i 0 32 for (i = 0; i < 3; i++)
995 {
40 44 48 52 56
j 0 x x
m[i] = malloc
(sizeof (int)*2);
991
60 64 68 72 76 for (j = 0; j < 2; j++)
m 20 x x x x x {
m[i][j] = i + j;
987
80 84 88 92 96 }
x }
983

42
Another memory leak example
stack heap int i, j;
m[0] m[1] m[2]
999
20 24 28 32 36
int **m = (int**)
i 0 32 0 malloc(sizeof(int*)*3);

995
40 44 48 52 56 for (i=0; i<3; i++) {
j 0 x x
m[i] = (int*)
991
60 64 68 72 76 malloc(sizeof(int)*2);
m 20 x x x x x
for (j=0; j<2; j++) {
987
80 84 88 92 96
x m[i][j] = i + j;

983 }
}

43
Another memory leak example
stack heap int i, j;
m[0] m[1] m[2]
999
20 24 28 32 36
int **m = (int**)
i 0 32 0 1 malloc(sizeof(int*)*3);

995
40 44 48 52 56 for (i=0; i<3; i++) {
j 1 x x
m[i] = (int*)
991
60 64 68 72 76 malloc(sizeof(int)*2);
m 20 x x x x x
for (j=0; j<2; j++) {
987
80 84 88 92 96
x m[i][j] = i + j;

983 }
}

44
Another memory leak example
stack heap int i, j;
m[0] m[1] m[2]
999
20 24 28 32 36
int **m = (int**)
i 1 32 48 0 1 malloc(sizeof(int*)*3);

995
40 44 48 52 56 for (i=0; i<3; i++) {
j 1 x x
m[i] = (int*)
991
60 64 68 72 76 malloc(sizeof(int)*2);
m 20 x x x x x
for (j=0; j<2; j++) {
987
80 84 88 92 96
x m[i][j] = i + j;

983 }
}

45
Another memory leak example
stack heap int i, j;
m[0] m[1] m[2]
999
20 24 28 32 36
int **m = (int**)
i 1 32 48 0 1 malloc(sizeof(int*)*3);

995
40 44 48 52 56 for (i=0; i<3; i++) {
j 0 x 1 x
m[i] = (int*)
991
60 64 68 72 76 malloc(sizeof(int)*2);
m 20 x x x x x
for (j=0; j<2; j++) {
987
80 84 88 92 96
x m[i][j] = i + j;

983 }
}

46
Another memory leak example
stack heap int i, j;
m[0] m[1] m[2]
999
20 24 28 32 36
int **m = (int**)
i 1 32 48 0 1 malloc(sizeof(int*)*3);

995
40 44 48 52 56 for (i=0; i<3; i++) {
j 1 x 1 2 x
m[i] = (int*)
991
60 64 68 72 76 malloc(sizeof(int)*2);
m 20 x x x x x
for (j=0; j<2; j++) {
987
80 84 88 92 96
x m[i][j] = i + j;

983 }
}

47
Another memory leak example
stack heap int i, j;
m[0] m[1] m[2] int **m = malloc
999
20 24 28 32 36 (sizeof (int*)*3);
i 2 32 48 84 0 1 for (i = 0; i < 3; i++)
995 {
40 44 48 52 56
j 1 x 1 2 x
m[i] = malloc
(sizeof(int)*2);
991
60 64 68 72 76 for (j = 0; j < 2; j++)
m 20 x x x x x {
m[i][j] = i + j;
987
80 84 88 92 96 }
x }
983

48
Another memory leak example
stack heap int i, j;
m[0] m[1] m[2] int **m = malloc
999
20 24 28 32 36 (sizeof (int*)*3);
i 2 32 48 84 0 1 for (i = 0; i < 3; i++)
995 {
40 44 48 52 56
j 0 x 1 2 x
m[i] = malloc
(sizeof(int)*2);
991
60 64 68 72 76 for (j = 0; j < 2; j++)
m 20 x x x x x {
m[i][j] = i + j;
987
80 84 88 92 96 }
x 2 }
983

49
Another memory leak example
stack heap int i, j;
m[0] m[1] m[2] int **m = malloc
999
20 24 28 32 36 (sizeof (int*)*3);
i 2 32 48 84 0 1 for (i = 0; i < 3; i++)
995 {
40 44 48 52 56
j 1 x 1 2 x
m[i] = malloc
(sizeof(int)*2);
991
60 64 68 72 76 for (j = 0; j < 2; j++)
m 20 x x x x x {
m[i][j] = i + j;
987
80 84 88 92 96 }
x 2 3 }
983

50
Function ends with memory leak
stack heap int i, j;
m[0] m[1] m[2] int **m = malloc
999
20 24 28 32 36 (sizeof (int*)*3);
32 48 84 0 1 for (i = 0; i < 3; i++)
995 {
40 44 48 52 56
m[i] = malloc
x 1 2 x (sizeof(int)*2);
991
60 64 68 72 76 for (j = 0; j < 2; j++)
x x x x x {
m[i][j] = i + j;
987
80 84 88 92 96 }
x 2 3 }
983

51
Trying to fix using free (attempt 1)
stack heap …
m[0] m[1] m[2]
999
20 24 28 32 36
free(m);
i 2 32 48 84 0 1 m = NULL;

995
40 44 48 52 56
j 1 x 1 2 x
991
60 64 68 72 76
m 20 x x x x x
987
80 84 88 92 96
x 2 3
983

52
Trying to fix using free (attempt 1)
stack heap …

999
20 24 28 32 36
free(m);
i 2 0 1 m = NULL;

995
40 44 48 52 56
j 1 x 1 2 x
991
60 64 68 72 76
m x x x x x
987
80 84 88 92 96
x 2 3
983

53
Function still ends with memory leak
stack heap …

999
20 24 28 32 36
free(m);
i 0 1 m = NULL;

995
40 44 48 52 56
j x 1 2 x
991
60 64 68 72 76
m x x x x x
987
80 84 88 92 96
x 2 3
983

54
Correct: freeing all allocated memory
stack heap …

999
20 24 28 32 36
for (i=0; i<3; i++)
i 2 32 48 84 0 1 {
free(m[i]);
995
40 44 48 52 56 m = NULL;
j 1 x 1 2 x }
free(m);
991
60 64 68 72 76 M = NULL;
m 20 x x x x x
987
80 84 88 92 96
x 2 3
983

55
Freeing all allocated memory
stack heap …

999
20 24 28 32 36
for (i=0; i<3; i++)
i 0 0 48 84 {
free(m[i]);
995
40 44 48 52 56 m = NULL;
j 1 x 1 2 x }
free(m);
991
60 64 68 72 76 m = NULL;
m 20 x x x x x
987
80 84 88 92 96
x 2 3
983

56
Freeing all allocated memory
stack heap …

999
20 24 28 32 36
for (i=0; i<3; i++)
i 1 0 0 84 {
free(m[i]);
995
40 44 48 52 56 m = NULL
j 1 x x }
free(m);
991
60 64 68 72 76 m = NULL;
m 20 x x x x x
987
80 84 88 92 96
x 2 3
983

57
Freeing all allocated memory
stack heap …

999
20 24 28 32 36
for (i=0; i<3; i++)
i 2 0 0 0 {
free(m[i]);
995
40 44 48 52 56 m = NULL
j 1 x x }
free(m);
991
60 64 68 72 76 m = NULL;
m 20 x x x x x
987
80 84 88 92 96
x
983

58
Freeing all allocated memory
stack heap …

999
20 24 28 32 36
for (i=0; i<3; i++)
i 2 {
free(m[i]);
995
40 44 48 52 56 m = NULL;
j 1 x x }
free(m);
991
60 64 68 72 76 M = NULL;
m 20 x x x x x
987
80 84 88 92 96
x
983

59
 function ends with no memory leak 
stack heap …

999
20 24 28 32 36
for (i=0; i<3; i++)
i {
free(m[i]);
995
40 44 48 52 56 m = NULL;
j x x }
free(m);
991
60 64 68 72 76 M = NULL;
m x x x x x …
return …;
987
80 84 88 92 96
x
983

60

You might also like