The Standard Library
The standard library is like a toolbox — you don't need to forge your own hammer and screwdriver, just pick them up and use them. Understanding what each tool does helps you choose the right one.
math.h Mathematical Operations
Using math functions requires including the header and linking the math library:
#include <math.h>
Compile with the -lm flag: gcc program.c -lm.
Common Math Functions
| Function | Purpose | Example |
|---|---|---|
fabs(x) |
Absolute value | fabs(-3.5) → 3.5 |
sqrt(x) |
Square root | sqrt(16.0) → 4.0 |
pow(x, y) |
x raised to the power y | pow(2.0, 10.0) → 1024.0 |
ceil(x) |
Round up | ceil(3.2) → 4.0 |
floor(x) |
Round down | floor(3.8) → 3.0 |
round(x) |
Round to nearest | round(3.5) → 4.0 |
fmod(x, y) |
Floating-point remainder | fmod(7.5, 2.5) → 0.0 |
log(x) |
Natural logarithm | log(2.718) → 1.0 |
log10(x) |
Common logarithm | log10(100.0) → 2.0 |
sin(x) |
Sine | sin(3.14/2) → 1.0 |
cos(x) |
Cosine | cos(0.0) → 1.0 |
tan(x) |
Tangent | tan(0.0) → 0.0 |
rad = deg * 3.14159265 / 180.0.
Example
Calculate the distance between two points:
#include <stdio.h>
#include <math.h>
typedef struct {
double x;
double y;
} Point;
double distance(Point a, Point b) {
double dx = a.x - b.x;
double dy = a.y - b.y;
return sqrt(dx * dx + dy * dy);
}
int main(void) {
Point p1 = {3.0, 4.0};
Point p2 = {0.0, 0.0};
printf("Distance: %.2f\n", distance(p1, p2));
return 0;
}
Distance: 5.00
stdlib.h General Utilities
Random Numbers
int rand(void);
void srand(unsigned int seed);
rand() returns a pseudo-random integer between 0 and RAND_MAX. Without calling srand, every run of the program produces the same sequence of random numbers.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void) {
srand((unsigned int)time(NULL));
for (int i = 0; i < 5; i++) {
printf("%d ", rand());
}
printf("\n");
for (int i = 0; i < 5; i++) {
printf("%d ", rand() % 100);
}
printf("\n");
return 0;
}
1804289383 846930886 1681692777 1714636915 1957747793
83 86 77 15 93
rand() % N doesn't provide good randomness in most implementations — lower bits may follow a pattern. For scenarios requiring high-quality randomness, use a more advanced random number generator.
Type Conversion
| Function | Purpose |
|---|---|
atoi(str) |
String to int |
atol(str) |
String to long |
atof(str) |
String to double |
strtol(str, &end, base) |
String to long (with base) |
strtod(str, &end) |
String to double (with error detection) |
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int a = atoi("42");
double b = atof("3.14");
long c = strtol("0xFF", NULL, 16);
printf("a = %d\n", a);
printf("b = %.2f\n", b);
printf("c = %ld\n", c);
char *end;
long d = strtol("123abc", &end, 10);
printf("d = %ld, Unconverted part: %s\n", d, end);
return 0;
}
a = 42
b = 3.14
c = 255
d = 123, Unconverted part: abc
atoi cannot detect errors — invalid input returns 0, which is indistinguishable from a legitimate 0. Prefer strtol/strtod, which let you determine whether conversion succeeded via the end pointer.
Dynamic Memory Management
void *malloc(size_t size);
void *calloc(size_t count, size_t size);
void *realloc(void *ptr, size_t size);
void free(void *ptr);
malloc: allocatessizebytes, does not initializecalloc: allocatescount*sizebytes, initializes all to 0realloc: resizes previously allocated memory, may move the addressfree: releases memory
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *arr = calloc(5, sizeof(int));
if (arr == NULL) {
return 1;
}
for (int i = 0; i < 5; i++) {
arr[i] = i * 10;
}
int *new_arr = realloc(arr, 10 * sizeof(int));
if (new_arr == NULL) {
free(arr);
return 1;
}
arr = new_arr;
for (int i = 5; i < 10; i++) {
arr[i] = i * 10;
}
for (int i = 0; i < 10; i++) {
printf("%d ", arr[i]);
}
printf("\n");
free(arr);
return 0;
}
0 10 20 30 40 50 60 70 80 90
realloc fails, it returns NULL but the original memory is not freed! So you must use a temporary variable to receive realloc's return value — on failure, you can still free the original memory.
Sorting and Searching
qsort
void qsort(void *base, size_t count, size_t size,
int (*compare)(const void *, const void *));
Comparison function rules: return negative if a < b, 0 if equal, positive if a > b.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int cmp_int(const void *a, const void *b) {
return *(const int *)a - *(const int *)b;
}
int cmp_str(const void *a, const void *b) {
return strcmp(*(const char )a, *(const char )b);
}
int main(void) {
int nums[] = {42, 17, 8, 95, 3, 61};
int n = sizeof(nums) / sizeof(nums[0]);
qsort(nums, n, sizeof(int), cmp_int);
for (int i = 0; i < n; i++) {
printf("%d ", nums[i]);
}
printf("\n");
const char *names[] = {"Alice", "Bob", "Charlie", "Diana"};
int m = sizeof(names) / sizeof(names[0]);
qsort(names, m, sizeof(char *), cmp_str);
for (int i = 0; i < m; i++) {
printf("%s ", names[i]);
}
printf("\n");
return 0;
}
3 8 17 42 61 95
Alice Bob Charlie Diana
bsearch
void *bsearch(const void *key, const void *base, size_t count,
size_t size, int (*compare)(const void *, const void *));
Performs binary search on a sorted array. Returns a pointer to the found element, or NULL if not found.
#include <stdio.h>
#include <stdlib.h>
int cmp_int(const void *a, const void *b) {
return *(const int *)a - *(const int *)b;
}
int main(void) {
int nums[] = {3, 8, 17, 42, 61, 95};
int n = sizeof(nums) / sizeof(nums[0]);
int key = 42;
int *result = bsearch(&key, nums, n, sizeof(int), cmp_int);
if (result != NULL) {
printf("Found %d at index %ld\n", key, result - nums);
} else {
printf("%d not found\n", key);
}
return 0;
}
Found 42 at index 3
time.h Time Handling
Time Functions
| Function/Type | Purpose |
|---|---|
time_t |
Time type (usually seconds since 1970-01-01) |
time(&t) |
Get current time |
clock() |
Get CPU clock ticks used by program |
localtime() |
Convert to local time struct |
gmtime() |
Convert to UTC time struct |
strftime() |
Format time as string |
difftime() |
Calculate difference between two times (seconds) |
struct tm
struct tm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
tm_mon ranges from 0-11 (0 = January), tm_year is the number of years since 1900, and tm_wday uses 0 for Sunday.
Example
#include <stdio.h>
#include <time.h>
int main(void) {
time_t now = time(NULL);
struct tm *local = localtime(&now);
char buf[64];
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", local);
printf("Current time: %s\n", buf);
printf("Today is day %d of the week (0=Sun)\n", local->tm_wday);
return 0;
}
Current time: 2025-03-15 14:30:22
Today is day 6 of the week (0=Sun)
Program Timing
#include <stdio.h>
#include <time.h>
int main(void) {
clock_t start = clock();
volatile long sum = 0;
for (long i = 0; i < 100000000L; i++) {
sum += i;
}
clock_t end = clock();
double elapsed = (double)(end - start) / CLOCKS_PER_SEC;
printf("Elapsed: %.3f seconds\n", elapsed);
return 0;
}
Elapsed: 0.235 seconds
clock() measures CPU time, not wall-clock time. If the program has sleep or I/O waits, that time is not counted. Use difftime to measure actual elapsed time.
ctype.h Character Handling
| Function | Test Condition |
|---|---|
isalpha(c) |
Letter |
isdigit(c) |
Digit |
isalnum(c) |
Letter or digit |
isupper(c) |
Uppercase letter |
islower(c) |
Lowercase letter |
isspace(c) |
Whitespace (space, tab, newline, etc.) |
ispunct(c) |
Punctuation |
isprint(c) |
Printable character |
toupper(c) |
Convert to uppercase |
tolower(c) |
Convert to lowercase |
These functions require an unsigned char value or EOF as their argument. Passing a negative char value is undefined behavior.
Example
Count letters, digits, and other characters in a string:
#include <stdio.h>
#include <ctype.h>
int main(void) {
char str[] = "Hello, World! 123";
int letters = 0, digits = 0, others = 0;
for (int i = 0; str[i] != '\0'; i++) {
if (isalpha((unsigned char)str[i])) {
letters++;
} else if (isdigit((unsigned char)str[i])) {
digits++;
} else {
others++;
}
}
printf("Letters: %d, Digits: %d, Others: %d\n", letters, digits, others);
return 0;
}
Letters: 10, Digits: 3, Others: 6
assert.h Assertions
void assert(int expression);
When the expression is false, the program terminates and prints an error message (file name, line number, expression). Defining the NDEBUG macro before #include <assert.h> disables all assertions.
#include <stdio.h>
#include <assert.h>
double safe_divide(double a, double b) {
assert(b != 0 && "Divisor cannot be zero");
return a / b;
}
int main(void) {
printf("10 / 2 = %.1f\n", safe_divide(10.0, 2.0));
printf("10 / 0 = %.1f\n", safe_divide(10.0, 0.0));
return 0;
}
10 / 2 = 5.0
Assertion failed: b != 0 && "Divisor cannot be zero", file main.c, line 5
assert gets printed, so writing "Divisor cannot be zero" is more effective than a comment — when the assertion fails, you can immediately see the reason.
❓ FAQ
srand(time(NULL)) to seed with the current time.📖 Summary
math.hprovides math functions; trig functions use radians; compile with-lmrand/srandgenerate pseudo-random numbers; usesrand(time(NULL))to seed with timestrtol/strtodare safer thanatoi/atofand can detect conversion errorsqsort/bsearchare the standard library's sorting and searching tools, requiring custom comparison functionsassertis for development debugging; disable in release builds viaNDEBUG
📝 Exercises
- Write a program that generates 100 random integers from 1 to 1000, sorts them with qsort, and outputs the maximum and minimum values
- Write a program that uses clock() to compare the time difference between bubble sort and qsort on 10,000 integers
- Write a function that uses ctype.h functions to implement string case conversion (input a string, output all-uppercase and all-lowercase versions)



