1Learning Outcomes¶
Identify generic functions used in heap memory management.
Understand why generics support general-purpose code in C.
In this chapter, we practice our C memory skills with two more concepts involving pointers.
As mentioned in a previous chapter, normally pointers can only point to one type. Declaring int *p; tells us that p should point to an int value, and it also determines p’s operation with operators like pointer arithmetic (what byte stride to adjust by) and dereferencing (how many bytes to read).
In this section, we discuss the void * pointer, a generic pointers that can point to anything. Generics support general-purpose code by reducing code duplication. Generics are used throughout C to sort an array of any type, search an array of any type, free memory containing data of any type, and more.
By defining one function for each of these use cases, we can call that function across variable types. This helps us make improvements in a single place, instead of multiple very similar places. In doing so, we must further understand memory so that we avoid bugs when writing our own generics.
We have two goals for generics:
Generics should generally regardless of argument type.
Generics should work by accessing blocks of memory, regardless of the data type stored in those blocks.
The first of these is self-evident; the second will become clear later.
2Generic functions in standard C libraries¶
While we said that we would use generics sparingly to avoid program bugs, you have already encountered your first generics! Check out the stdlib.h heap memory management functions:
void *malloc(size_t n)void free(void *ptr)void *realloc(void *ptr, size_t size)
These functions are generic functions (or generics[1] for short) because they return or use do not assume anything about the type of the memory being allocated or freed. As described in a previous section, we cast the return values of malloc and realloc calls to the appropriate pointer types and use them in local, typed pointer variables.
3Motivation: swap_int, swap_short, swap_string¶
3.1swap_int¶
Suppose we write a function swap_int for swapping integers:
1 2 3 4 5void swap_int(int *ptr1, int *ptr2) { int temp = *ptr1; *ptr1 = *ptr2; *ptr2 = temp; }
Because C is pass-by-value, in order to swap integers declared in a different function scope, we pass in arguments as pointers. For example, we could call swap_int as follows:
int x = 2;
int y = 5;
swap_int(&x, &y);The slidedeck below traces through a toy example that assumes that initially, x stores 2 at address 0x100 and y stores 5 at 0x104. After the swap_int call, x and y are still at the same addresses but have swapped values to 5 and 2, respectively.
Click below to show the explanation of the animation.
Explanation
The function swap_int leverages pointer dereferencing and its own temp local variable to update the correct values in memory.
Function call: The addresses of the local variables
xandyare passed to theswap_intfunction call asptr1andptr2, respectively.ptr1stores address0x100, andptr2stores address0x104.Line 2: The local variable
tempmakes a copy of the value atptr1, which is2(currently stored at address0x100).Line 3: Set the value at
ptr1to a copy of the value atptr2. The right-hand side,*ptr2, dereferencesptr2and evaluates to5(because those are theintbytes at the address0x104). The left-hand side,*ptr1, denotes the target location—the integer bytes at address0x100.Line 4: Set the value at
ptr2to a copy oftemp. The right-hand side evaluates to anint—the value2. The left-hand side,*ptr2, denotes the target location to store these bytes—at address0x104.
3.2swap_short¶
Next we write a function swap_short for swapping shorts (the integer type, not the fashion statement):
1 2 3 4 5void swap_short(short *ptr1, short *ptr2) { short temp = *ptr1; *ptr1 = *ptr2; *ptr2 = temp; }
Aside from the type declarations of ptr1, ptr2, and temp, the logic remains similar.
3.3swap_string¶
The logic is still similar with swap_string, which “swaps strings”.
1 2 3 4 5void swap_string(char **ptr1, char **ptr2) { char *temp = *ptr1; *ptr1 = *ptr2; *ptr2 = temp; }
Instead of making copies of char bytes, this function swaps the addresses of two char * variables (i.e., pointers to C strings). We could call swap_str with the below code.
char *s1 = "CS";
char *s2 = "61C";
swap_string(&s1, &s2);Figure 1 illustrates a toy example of memory at the start of the call to swap_string; Figure 2 illustrates the memory right before the call returns.

Figure 1:swap_string is called.

Figure 2:Right before swap_string call returns.
Click below to show the explanation of Figure 1 and Figure 2.
3.4Can we write a generic swap?¶
These three functions demonstrate that at a high level, swapping two values of the same data type follow the same logic. We would like to write a function that can perform a generic swap. Let’s read on!
Java also supports generics to (among other things) support the creation of data structures that can hold any reference type, e.g.,
DataStructure<T>