Arrays Dynamic Memory Allocation 2 D arrays malloc

  • Slides: 43
Download presentation
Arrays & Dynamic Memory Allocation 2 D arrays malloc, calloc, realloc, and free Linked

Arrays & Dynamic Memory Allocation 2 D arrays malloc, calloc, realloc, and free Linked Lists Pointers to pointers

2 D Arrays • Used for tables, matrices • Specify number of rows, columns

2 D Arrays • Used for tables, matrices • Specify number of rows, columns • 0 -indexing for rows and columns • an array of 1 D arrays • Examples: int matrix[10][5]; // 5 rows, 10 cols char conversion. Table[26]; • Initializer lists: specify bracketed lists for each row, or omit row brackets – values will fill array row by row int arr[3][4] = {{0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}}; int arr[3][4] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};

2 D Array Example int main() { // 4 rows, 2 columns int arr[4][2]

2 D Array Example int main() { // 4 rows, 2 columns int arr[4][2] = {{1, 2}, {3, 4}, {5, 6}, {7, 8}}; // print array for(int i = 0; i < 4; i++) { for(int j = 0; j < 2; j++) { printf("%dt", arr[i][j]); } printf("n"); } } Output: 1 2 3 4 5 6 7 8

2 D Array Example • In a 2 D array, you can omit the

2 D Array Example • In a 2 D array, you can omit the number of rows if the array is completely initialized when declared • If a parameter is a 2 D array, the number of rows may be omitted: int sum 2 D(int arr[][LEN], int n) { int sum = 0; for(int i = 0; i < n; i++) { for(int j = 0; j < LEN; j++) { sum += arr[i][j]; } } return sum; }

Variable Length Array Parameter • Starting in C 99: specify array dimension with non-constant

Variable Length Array Parameter • Starting in C 99: specify array dimension with non-constant int sum. Array(int n, int a[n]) {// must put n first . . . } int sum 2 DArray(int n, int m, int a[n][m]) { int sum = 0; for(int i = 0; i < n; i++) { n, m must come before a in parm list for(int j = 0; j < m; j++) { sum += a[i][j]; } } return sum; }

Pointers & 2 DArrays • Arrays stored in row-major order • To initialize elements

Pointers & 2 DArrays • Arrays stored in row-major order • To initialize elements in 2 D array: • nested for loops • single loop another option with pointer arithmetic: int arr[NUM_ROWS][NUM_COLS]; int *p; for(p = &a[0][0]; p <= &arr[NUM_ROWS-1][NUM_COLS-1]; p++) { *p = 3; } • No real advantage of second approach – less readable • Use a pointer to a row to process that row: p = &arr[i][0]; // for valid row number i. OR p = arr[i]; // p points to row i

Example int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; int

Example int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; int *p; for(int i = 0; i < 3; i++) { p = &arr[i][0]; for(int j = 0; j < 3; j++) printf("%d ", *(p+j)); printf("n"); } Output: 1 2 3 4 5 6 7 8 9 for(int i = 0; i < 3; i++) { p = arr[i]; for(int j = 0; j < 3; j++) printf("%d ", *(p+j)); printf("n"); }

Row Processing: 2 D Array • So arr[i] is address of row i in

Row Processing: 2 D Array • So arr[i] is address of row i in 2 D array arr • Can pass arr[i] to function that takes 1 D array as argument • To set all values of row i to zero: int arr[NUM_ROWS][NUM_COLS], *p, i; … for (p = arr[i]; p < arr[i] + NUM_COLS; p++) *p = 0;

2 D Array Name as Pointer • Any array name can be used as

2 D Array Name as Pointer • Any array name can be used as a pointer • arr is a pointer to arr[0] • arr is regarded as a 1 D array with elements that are 1 D arrays • arr has type int (*) [NUM_COLS] • To set elements in column c to zero: for(p = arr; p < arr + NUM_ROWS; p++) { (*p)[c] = 0; } • Since p points to a row of arr, incrementing p assigns p to point to next row

Dynamic Memory

Dynamic Memory

Dynamic Memory • We can allocate memory on the heap during program execution •

Dynamic Memory • We can allocate memory on the heap during program execution • Often used for strings, arrays, structs, linked lists, trees • <stdlib. h> declares 3 memory allocation functions: malloc: allocates an uninitialized block of memory calloc: allocates a block of memory and initializes to zero • Takes time to zero out memory – don't use unless needed realloc: resizes allocated block of memory • All three return type void * (a void pointer) • Return null pointer if block of requested size isn't found • NULL represents null pointer

Allocating Memory • prototype: void *malloc(size_t size); • mallocates contiguous size bytes and returns

Allocating Memory • prototype: void *malloc(size_t size); • mallocates contiguous size bytes and returns pointer to it • size_t: unsigned integer type (defined in <stdlib. h>) • void free(void *ptr); // free memory when done with it • Example: array of n ints int * a = malloc(n * sizeof(int)); // a holds address of first elt if(a != NULL) { // better: if(a) for(int i = 0; i < n; i++) a[i] = i; // Or: *(a+i) = i; } • Later - free memory: free(a);

Deallocation & Common Mistakes • free(a); // releases allocated block on heap • Free

Deallocation & Common Mistakes • free(a); // releases allocated block on heap • Free memory which has been dynamically allocated • Calling free() does not change value of pointer variable – reset it to NULL • Common mistakes: • dangling pointer: a pointer that contains the address of dynamic memory which has been freed • Can happen when there are multiple pointers to dynamically allocated memory • memory leak: memory is allocated and not freed up again • • system will eventually run out of memory can occur if memory is malloc'ed in a loop check return value of malloc/calloc/realloc to ensure it isn't NULL use valgrind to detect (more on valgrind in recitation) • double deallocation: attempting to deallocate memory pointed to by dangling pointer • uninitialized pointer: an uninitialized pointer contains garbage – trying to access random memory location cause unpredictable behavior

Memory Leak p = malloc(. . . ); q = malloc(. . . );

Memory Leak p = malloc(. . . ); q = malloc(. . . ); p = q; • no pointer to 1 st block (garbage) • Block cannot be freed • This sort of code leads to memory leaks

Dangling Pointer char *ptr 1 = malloc(10*sizeof(char)); // new string char *ptr 2 =

Dangling Pointer char *ptr 1 = malloc(10*sizeof(char)); // new string char *ptr 2 = ptr 1; . . . free(ptr 2); . . . strcpy(ptr 1, "hello"); • Two dangling pointers – point to same deallocated memory

String Functions char *my. Concat(const char *s 1, const char *s 2) { char

String Functions char *my. Concat(const char *s 1, const char *s 2) { char *result = malloc(strlen(s 1) + strlen(s 2) + 1); if(result == NULL) { printf("Error: malloc failedn"); exit(EXIT_FAILURE); } process termination: flushes output strcpy(result, s 1); streams, closes open streams strcat(result, s 2); Type man 3 exit at command prompt return result; for more info } • Function call: char *p = my. Concat("hello ", "world");

calloc • Prototype: void *calloc(size_t nmemb, size_t size); • Allocates space for nmemb elements,

calloc • Prototype: void *calloc(size_t nmemb, size_t size); • Allocates space for nmemb elements, each of size bytes • Initializes memory to 0

Dynamically Allocated Arrays • Particularly useful before C 99 standard allowed non-constant array sizes

Dynamically Allocated Arrays • Particularly useful before C 99 standard allowed non-constant array sizes int num. Vals = -1; double *data = NULL; do { printf("How many data values do you want to store? "); scanf("%d", &num. Vals); } while (num. Vals < 1); data = malloc(num. Vals * sizeof(double)); // uninitialized // data = calloc(num. Values, sizeof(double)); // cleared to zeros if(!data) printf("Error – malloc failedn");

realloc • Resize a dynamically allocated array • Prototype: void *realloc(void *ptr, size_t size);

realloc • Resize a dynamically allocated array • Prototype: void *realloc(void *ptr, size_t size); • ptr points to previously allocated memory block • if ptr is NULL, new block allocated and pointer to block returned • size = new size of block in bytes (can be larger or smaller than original size) • if size is 0 and ptr points to existing block, block is deallocated and NULL is returned • Returns pointer to new block or NULL on failure • Attempts to use original block of memory, shrinking or expanding it in place

Linked List • chain of structs (nodes) in which each node contains a pointer

Linked List • chain of structs (nodes) in which each node contains a pointer to next node • last node contains null pointer • Need pointer to head of list (1 st element) • Advantages over array: • easy to increase size of list • easy to insert or delete element at any location • Disadvantages: • Slow access to ith element of list

Define List Node • Nodes contain data and pointer to next node in list

Define List Node • Nodes contain data and pointer to next node in list struct node { int value; struct node *next; }; • node must be tag, not typedef alias, to allow declaration of type of next pointer

Create Empty List • Sets list pointer to null, creating empty list struct node

Create Empty List • Sets list pointer to null, creating empty list struct node *first = NULL;

Exercise • Add first node to list – value for node is 10 •

Exercise • Add first node to list – value for node is 10 • allocate memory for new node • initialize node's fields • update list pointer

Exercise • Write code that adds a new element to the beginning of a

Exercise • Write code that adds a new element to the beginning of a list. Assume first is a pointer to the beginning of the list, and add 15 to the list.

Create List Node • List nodes are typically allocated dynamically and added to list

Create List Node • List nodes are typically allocated dynamically and added to list • Allocate memory for node • Store data in node • Insert node into list • Allocating memory: struct node *new_node = malloc(sizeof(struct node)); • Store data in node: new_node value = 10; OR: (*new_node). value = 10; OR: scanf("%d", &new_node value);

Insert Node at Beginning of List • new_node points to node we are inserting

Insert Node at Beginning of List • new_node points to node we are inserting • first always points to first node in list • here list was initially empty new_node next = first; first = new_node;

Insert Node at Beginning of List • new_node points to node we are inserting

Insert Node at Beginning of List • new_node points to node we are inserting • first points to first node in list new_node = malloc(sizeof(struct node)); new_node value = 20; new_node next = first; first = new_node;

Function: Insert Node at Beginning of List struct node *add. To. List(struct node *list,

Function: Insert Node at Beginning of List struct node *add. To. List(struct node *list, int n) { struct node *new. Node = malloc(sizeof(struct node)); if(new. Node == NULL) { printf("Error: malloc failedn"); exit(EXIT_FAILURE); } new. Node value = n; new. Node next = list; return new. Node; } • Store return value in first: first = add. To. List(first, 10); first = add. To. List(first, 20);

Exercise • Write a function that finds integer n in list, and returns a

Exercise • Write a function that finds integer n in list, and returns a pointer to the node that contains n. The function returns NULL if n is not in the list. struct node *search. List(struct node *list, int n) {

Search Linked List • Look for node containing value n struct node *search. List(struct

Search Linked List • Look for node containing value n struct node *search. List(struct node *list, int n) { struct node *p; for(p = list; p != NULL; p = p next) if(p value == n) return p; return NULL; }

Search Linked List struct node *search. List(struct node *list, int n) { for(; list!=

Search Linked List struct node *search. List(struct node *list, int n) { for(; list!= NULL && list value != n; list = list next) ; return list; } • loop body is empty • list is NULL if we reach end of list, so NULL is returned if n not found • more natural to use while loop: while(list != NULL && list value != n) list = list next; return list;

Deleting Node From Linked List • Easy to delete node from linked list •

Deleting Node From Linked List • Easy to delete node from linked list • 3 steps: • Locate the node • Maintain pointer to previous node (prev) and pointer to current node (cur) • Initially prev is NULL, cur is first (list pointer) • Update previous node's next pointer to bypass deleted node • Free the deleted node

Deleting Node From List • Assume list is as follows, n is 20 •

Deleting Node From List • Assume list is as follows, n is 20 • cur = list; prev = NULL; • while(cur != NULL && cur value !=n) prev = cur; cur = cur next;

Deleting Node From List • while(cur != NULL && cur value !=n) prev =

Deleting Node From List • while(cur != NULL && cur value !=n) prev = cur; cur = cur next; • loop terminates since cur value != n is false

Deleting Node: Steps 2 & 3 Bypass Deleted Node and Free It prev next

Deleting Node: Steps 2 & 3 Bypass Deleted Node and Free It prev next = cur next; free(cur);

Deleting Node From Linked List struct node *delete. Node(struct node *list, int n) {

Deleting Node From Linked List struct node *delete. Node(struct node *list, int n) { struct node *cur, *prev; for(cur = list, prev = NULL; cur != NULL && cur value != n; prev = cur, cur = cur next) ; if(cur == NULL) return list; // n not found if(prev == NULL) list = list next; // n in first node else prev next = cur next; // n in some other node free(cur); return list; }

Exercise • Write a function that returns the length of a linked list: int

Exercise • Write a function that returns the length of a linked list: int linked. Length(struct node *list) { // Your code here }

Ordered List • Nodes are maintained in order – decreasing or increasing • Inserting

Ordered List • Nodes are maintained in order – decreasing or increasing • Inserting is more difficult than inserting always at the beginning of the list • Exercise: Rewrite add. To. List assuming that the list of nodes is in increasing order.

Pointers to Pointers • If we want a function to modify a pointer, we

Pointers to Pointers • If we want a function to modify a pointer, we must pass a pointer to that pointer • The function add. To. List adds a new element to beginning of a list, and updates the list pointer, rather than returning the list pointer void add. To. List(struct node **list. Ref, int n) { struct node *new. Node = malloc(sizeof(struct node)); if(new. Node == NULL) { printf("Error: malloc failedn"); exit(EXIT_FAILURE); } // What goes here? ? }

Pointers to Pointers: Add to Beginning of List void add_to_list(struct node **list. Ref, int

Pointers to Pointers: Add to Beginning of List void add_to_list(struct node **list. Ref, int n) { struct node *new_node; new_node = malloc(sizeof(struct node)); if (new_node == NULL) { printf("Error: malloc failed in add_to_listn"); exit(EXIT_FAILURE); } new_node->value = n; new_node->next = *list. Ref; *list. Ref = new_node; } • Call: add. To. List(&first, 10);

Exercise • Write C code that adds a new node (containing your favorite integer)

Exercise • Write C code that adds a new node (containing your favorite integer) to the end of a linked list. Assume list points at the first element of the list. struct node *list =. . . ;

Exercise • Write a function that moves the last node in a list to

Exercise • Write a function that moves the last node in a list to the front of the linked list. head. Ref is a pointer to the list pointer. void move. Last. To. First(struct node **head. Ref) {. . .

Exercise • Write a function that reverses a linked list. head. Ref is a

Exercise • Write a function that reverses a linked list. head. Ref is a pointer to the list pointer. void reverse(struct node **head. Ref) {. . .