JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

A Tour of the C Standard Library

C the language is tiny on purpose; the standard library is the box of ready-made tools that comes with it. Take a guided tour of the pieces you will actually reach for, and learn the one habit that separates a working program from a dangerous one.

A small language with a big toolbox

You have now met the whole core of C: types and variables, operators, control flow, functions and recursion, arrays and strings, and structs and enums. What is striking is how little of it there is. The C language itself has no built-in command to print, no built-in way to read a file, no built-in square root. That is not an oversight — it is the design. The language is kept tiny so it can run anywhere, and everything else lives in the C standard library, a collection of ready-made functions that ships with every C compiler.

Think of the language as a bare workshop with a workbench and a vice, and the standard library as the drawer of tools you open every day: a hammer for printing, a saw for chopping up strings, a tape measure for sizes. You reach for a tool by including its header at the top of your file. A line like `#include <stdio.h>` tells the compiler "I am going to use the standard input/output tools," and from then on names like printf are available. Each drawer has a header: stdio.h for input and output, string.h for strings, stdlib.h for general utilities, math.h for math.

stdio: talking to the outside world

The drawer you have already been using is stdio, standard input/output. Its star is printf, which prints formatted text. The trick to printf is the format string: a template with little placeholders that start with a percent sign, each of which gets filled in by one of the values you pass after it. So `printf("x is %d\n", x)` prints the word "x is ", then the integer in x where %d sits, then a newline. The %d means "an int goes here"; %s means "a string goes here"; %f means "a floating-point number."

Going the other way, scanf reads typed input into your variables, and getchar reads one character at a time. Behind all three sits the idea of a stream: a flowing sequence of bytes, not a single lump. Your program comes with three streams already open — standard input (what you type), standard output (where printf goes), and standard error (where complaints go). Keeping normal output and error messages on separate streams is what lets you later send results to a file while errors still land on the screen.

string and the safety lesson hiding in it

Recall from the previous guide that a C string is just a character array that ends at the first zero byte — a null-terminated string. Because strings are not a built-in type, every common operation on them is a library function in string.h. strlen counts the characters up to the terminator; strcmp tells you whether two strings are equal; strcpy copies one string into another; strcat glues one onto the end of another. None of this is magic — each one is a small loop walking the array until it meets the zero byte, exactly the kind of loop you could write yourself.

Here the library teaches its hardest lesson, so let us be honest about it. strcpy copies characters from a source into a destination until it hits the terminator — but it has no idea how big your destination array is. If the source is longer than the room you set aside, strcpy keeps writing past the end of your array, scribbling over whatever memory sits next. This is a buffer overflow, one of the most common and most dangerous bugs in all of C: at best your program crashes, at worst an attacker steers those extra bytes to take it over.

stdlib, math, and asking for memory

The stdlib.h drawer is the junk drawer of genuinely useful odds and ends. atoi turns the string "42" into the integer 42; strtol does the same more carefully and tells you if it failed; rand gives you a pseudo-random number; qsort sorts an array; exit ends the program right now and hands an exit status back to whoever ran it. Next door, math.h holds the numerical tools — sqrt, pow, sin, floor — that operate on the floating-point numbers you met earlier. Link math separately on some systems with "gcc main.c -lm".

stdlib also holds the doorway to the most consequential idea in the whole library: asking the system for memory while the program runs. Until now, every array you made had its size baked in when you wrote the code. But what if you only learn how many items you need once the program is running — say, after reading a number the user types? The answer is dynamic memory allocation: you call malloc with a number of bytes, and the system hands back a region of exactly that size for you to use. calloc does the same but zeros the memory first; realloc resizes a block you already have.

int *scores = malloc(n * sizeof(int));   /* ask for n ints   */
if (scores == NULL) {                     /* ALWAYS check     */
    perror("malloc");                     /* report the error */
    return 1;                             /* fail honestly    */
}
/* ... use scores[0] .. scores[n-1] ... */
free(scores);                             /* give it back     */
The malloc / check / use / free pattern: every block you take, you check, and every block you take, you give back.

Two rules ride along with malloc, and skipping either bites you later. First, malloc can fail — if the system has no memory to give, it returns a special "nothing here" value, and using it crashes your program, so you must check the result before you touch it, exactly as the snippet does. Second, memory you take with malloc is not cleaned up automatically; when you are done you must hand it back with free, or your program slowly hoards memory it will never use again. The deeper machinery — how malloc finds that memory, what really happens when you free it, what a leak costs — is the subject of its own rung. For now, learn the rhythm: take, check, use, give back.

The one habit: check what the library tells you

If you take one habit from this whole rung, take this one: library functions report failure, and you must listen. C has no exceptions that leap out and stop the program for you; instead a function signals trouble through its return value. malloc returns a "nothing" value when it is out of memory. fopen returns the same when it cannot open a file. strtol returns zero and sets a flag. The polite fiction that calls always succeed is how beginner programs quietly corrupt data and crash much later, far from the real cause.

When a function needs to say more than "it failed" — to say why it failed — many library and system calls set a global variable called errno to a code naming the specific problem, like "file not found" or "permission denied." You read it right after the failing call and turn it into a message with perror or strerror. This is the classic return code plus errno pattern: the return value says yes-or-no, and errno says what went wrong.

  1. Call the function and store its return value — do not throw it away on the same line.
  2. Compare that value against the failure signal the documentation names (NULL for malloc and fopen, a negative number for many system calls).
  3. On failure, report it — read errno with perror if the call sets it — and decide honestly whether to recover or stop, rather than blundering on with a bad value.