Lab 3 Extra Problems

These optional problems are an opportunity to further exercise your understanding of arrays, pointers and the heap.

1) Additional Code Study: Heap Use

About 15 minutes Let's take a look at code that dynamically allocates memory on the heap using malloc, strdup and realloc. Specifically, we'll look at heap.c, which you can run or trace in gdb, containing a sample function called remove_first_occurrence.

The function remove_first_occurrence(text, pattern) returns a string which is a copy of text with the first occurrence of pattern removed. The returned string is heap-allocated and becomes the client's responsibility to free when done.

1   char *remove_first_occurrence(const char *text, 
2       const char *pattern) {
3
4       char *found = strstr(text, pattern);
5
6       if (!found) {
7           char *result = strdup(text);
8           assert(result != NULL);
9           return result;
10      }
11
12      int nbefore = found - text;
13      int nremoved = strlen(pattern);
14      char *result = malloc(strlen(text) + 1 - nremoved);
15      assert(result != NULL);
16
17      strncpy(result, text, nbefore);
18      strcpy(result + nbefore, found + nremoved);
19      return result;
20  }

Work through the following questions to understand this code. Ask questions about anything confusing!

2) Code study: calloc

About 15-20 minutes Let's take a look at the implementation of calloc, an alternative to the malloc memory-allocating function, to better understand how heap memory allocation works. First, read the manual page for the function (man calloc) and then review the implementation for musl_calloc shown below (based on code from musl):

1   void *musl_calloc(size_t nelems, size_t elemsz) {
2       if (nelems && (elemsz > SIZE_MAX / nelems)) {
3           return NULL;
4       }
5       size_t total = nelems * elemsz;
6       void *p = malloc(total);
7       if (p != NULL) {
8           long *wp;
9           size_t nw = (total + sizeof(*wp) - 1) / sizeof(*wp);
10          for (wp = p; nw != 0; nw--, wp++) {
11              *wp = 0;
12          }
13      }
14      return p;
15  }

The calloc interface is presented as allocating an array of nelems elements each of size elemsz bytes. It initializes the memory contents to zero, which is the feature that distinguishes calloc from ordinary malloc.

Here are some issues to go through to understand the operation of this function.

3) Calloc Challenge

About 20-30min The challenge.c file contains the calloc function from the earlier problem. The task is to modify the implementation to fill the memory with a specified pattern, instead of just 0s. Here's what the function prototype could look like:

void *musl_calloc2(size_t nelems, size_t elemsz, unsigned long pattern, int width) {
    ...
}

The first two parameters are the same, but the last two are new. pattern would be a bit pattern, between 0 and 64 bits. width would be the width (in bits) of that pattern. Only the least-significant width bits should be used as the pattern, even if other bits are non-zero. In other words, you shouldn't assume what is in the sizeof(long) - width other bits.

This new version of calloc should fill in the memory with the pattern of the given size. For instance, if the pattern is 0x8 and the width is 4, if you allocate 16 bytes of memory, each byte should look like 0x8888888888888888. The pattern is used to fill in every width bits of the allocated memory. Therefore, if you allocate 16 bytes of memory with the pattern 0x8000 and width 16, each byte should look like 0x8000800080008000. If the pattern does not fit evenly into a long, it should be copied in whole as much as possible from right to left. For example, if the pattern is 0x15 with width 5 (0b10101), each byte should look like 0x0ad6b5ad6b5ad6b5 (note the 4 leading zeros).

This task should exercise your bit and memory skills. Give it a shot!