Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The C Standard Library


You can access the slides 🖼️ for this lecture.

Here we discuss the C standard library, a collection of functions to accomplish various tasks when writing C programs.

Introduction

The C standard library, also shortened as libc, provides a set of pre-written functions to accomplish a plethora of low-level tasks in C. We have seen a few of these functions already: printf to print to the console, malloc to allocate memory, and a few others. These functions are portable, and you can use them with any compiler, operating system, and target architecture. The libc functions fall within a series of broad categories:

  • Input/output (stdio.h)
  • Memory management (stdlib.h)
  • String and memory manipulation (string.h)
  • Mathematical functions (math.h)
  • File handling (fcntl.h, unistd.h)
  • Time and date (time.h)

To use the functions from the libc, the programmer needs to include the relevant header .h files. Here we focus on some memory/string manipulation functions, because they have security implications.

String Copy

char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);

The function strcpy lets the programmer perform a string copy. It takes as parameters the destination buffer and the source string. The destination should be a buffer with enough space to accommodate a copy of the source. A variant is strncpy, which takes another parameter, n. strncpy will do the copy up to a maximum of n characters.

You can see an example of the usage of both functions below:

#include <string.h>
/* ... */
char *string1 = "hello";
char *string2 = string1;   // this is not a string copy!
char string3[10];          // allocated space of 10 bytes, it's called a buffer

/* not super safe, what happens if the size of string1 is larger than the
 * 10 bytes available in string3? */
strcpy(string3, string1);

/* better */
strncpy(string3, string1, 10);

printf("string1 @%p: %s\n", string1, string1); // string1 @1234: hello
printf("string2 @%p: %s\n", string2, string2); // string2 @1234: hello
printf("string3 @%p: %s\n", string3, string3); // string3 @5678: hello

We have a string named string1, containing "hello". Look at how string2 is constructed: please note that this is not a string copy: the assignment simply places the value of string1, which is a pointer, into string2. We end up with another pointer pointing to the original string:

If you want to make a proper string copy, i.e. to copy all characters from one memory location to another, you need to use strcpy. This is what happens to string3: it points to a buffer of 10 characters which is enough to accommodate a copy of the original string. Then we call strcpy, which will in effect copy the source into the destination, including the termination character. Note that strcpy can be dangerous when the source string is larger than the destination buffer: the function will blindly copy the entire source string and will overflow the destination buffer. To help avoid this, you can use strncpy, and pass as its last parameter the size of the destination buffer.

String Concatenation

char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src, size_t n);

To concatenate one string to another, you can use strcat. It takes two strings as parameters, a destination and a source. After the call, the destination string will contain the concatenation of the original destination string, followed by the source string. Once again, make sure the destination buffer has enough space to accommodate the sum of characters of the original destination, the source, plus one termination character. If you want to concatenate up to a certain number of characters, you can use strncat. Below is an example of the usage of these functions:

#include <string.h>

/* ... */

char world[6] = "world";
char s1[32];
char s2[32];

strcpy(s1, "hello ");
strcpy(s2, "hello ");

strcat(s1, world);       // not very safe
strncat(s2, world, 32 - strlen(s2));  // better

printf("s1: %s\n", s1); // hello world
printf("s2: %s\n", s2); // hello world

We have 2 buffers of 32 bytes each, s1 and s2. We start by placing the string "hello " in each buffer with strcpy. Then we concatenate s1 and the "world" string, resulting in s1 being "hello world". We also have a safer example with strncat concatenating s2 and the "world" string. See how we calculate the maximum number of characters to concatenate: it is the size of the destination buffer (32 bytes) minus the number of characters already present in it, obtained with the strlen function that returns the length of a string.

Copying Memory

void *memcpy(void *dest, void *src, size_t n);

To copy memory from one place to another we can use the memcpy function. It’s better to use that function than to copy manually, because it is highly optimised for speed. memcpy copies n bytes from the source src into the destination dest. Note that the source and destination are void * generic pointers so they can point to anything. Here is an example of the usage of memcpy:

# include <string.h>

typedef struct {
    int m1; int m2; int m3; float f1;
} mystruct;

int main() {
  int array_size = 10;
  mystruct array1[array_size];
  mystruct array2[array_size];

  for(int i=0; i<array_size; i++) { // initialise all of array1's members
    array1[i].m1 = /* ... */;
    array1[i].m2 = /* ... */;
    array1[i].m3 = /* ... */;
    array1[i].f1 = /* ... */;
  }

  // Assume we want to make a copy of array1 into array2: using memcpy is easier
  // (and faster) than using a loop:
  memcpy(array2, array1, array_size*sizeof(mystruct)); 
}

We have 2 arrays of 10 elements, each element being a relatively large data structure. We populate the first array manually, then suppose we want to populate the second array with the same content. Because we know that 1-dimensional arrays are laid out contiguously in memory, we can achieve this quickly with a memcpy. So we call memcpy with the destination being array2 and the source being array1. Remember that in C, arrays are pointers so we can pass them as parameters to memcpy. We also calculate the number of bytes to copy: it is the number of elements in the arrays, multiplied by the size of each element obtained with sizeof.

Console Input

char *fgets(char *s, int size, FILE *stream);
int scanf(const char *format, ...);

To get a string of characters from the user, you can use fgets. It takes the destination buffer s as a parameter, as well as the maximum number of characters to write, size. The third parameter is a stream that we’ll use to indicate that the characters should come from the console, also called the standard input.

To get numbers from the user, you can use scanf. It takes a string describing the input format as its first parameter, followed by the addresses of the variables to fill with the input.

The code below illustrates the use of fgets and scanf:

int int1, int2;
double double1;
float float1;
char s[128];

printf("Please input a string:\n");
fgets(s, 128, stdin);

printf("Please input an integer:\n");
scanf("%d", &int1);

printf("Please input a float:\n");
scanf("%lf", &double1); /* make sure to use %lf for double and %f for float */

printf("Please enter an integer and a float separated by a space\n");
scanf("%d %f", &int2, &float1);

printf("You have entered: %d, %d, %lf, %f, and %s\n",
        int1, int2, double1, float1, s);

The first call to fgets here gets a string from the user and places it into a buffer of 128 characters. Then we have a few calls to scanf to get respectively an integer, a double, and, on a single line, an integer and a float separated by a space. The program then prints the values of each number and string input to the program.

More Libc Functions, Manual Pages

Obviously, we cannot make a comprehensive overview of all functions from the C standard library here. In the rest of the unit, there will be other functions that will be useful to you, either because we are seeing them in the lecture materials, or because you need to use them to complete the lab exercises. To search and explore what kind of functions are available, there are many solutions, but one good starting point is cppreference.com, which has a good section on the C language and its standard library.

Furthermore, if you want to know how to use a particular function of the libc, you can use the manual. In a Linux terminal, run the man command followed by the name of the function:

man <function name>

The man page of the function in question will be displayed, describing the function’s prototype, its behaviour, the headers you need to include to use the function, and the values it can return on success or on failure. To exit the man page, simply type q on your keyboard.

⚠️ If a libc function name clashes with a command line program name, you may need to look up a different section of the manual. An example here is the sync function, which flushes filesystem RAM caches to disk. sync() is a function of the libc, but sync is also a terminal program. man sync will show the man page for the terminal program, so to check the page for the libc function, look up section 2 of the manual:

man 2 sync