No new tools needed for this chapter. Everything runs on the gcc,
make, and valgrind you installed in f05/00.
Create a working directory
mkdir ~/c01-practice
cd ~/c01-practiceThis is where all of libtci lives. One .c file per function, one
shared header, one Makefile that builds the lot into a static
library. You will spend the entire chapter here.
The initial structure
Create two files: the header and the Makefile. The header starts empty apart from its include guard. The Makefile starts with the right flags and grows as you add source files.
Create libtci.h:
#ifndef LIBTCI_H
#define LIBTCI_H
#include <stddef.h>
#endif<stddef.h> is where size_t and NULL are declared. It is a
minimal header — it does not pull in I/O or memory allocation; it
only defines the types and constants that other headers depend on.
Every function in libtci uses size_t for sizes and counts, so
this include belongs at the top.
The #ifndef LIBTCI_H ... #endif block is an include guard. If
another file includes libtci.h twice — directly or through a
chain of other headers — the preprocessor only processes it once.
Without the guard you get duplicate-declaration errors. Every header
you write should have one. The convention is to name the guard after
the filename in uppercase with the dot replaced by an underscore.
Create Makefile:
NAME = libtci.a
CC = gcc
CFLAGS = -Wall -Wextra -g -std=c99
AR = ar
ARFLAGS = rcs
SRCS =
OBJS = $(SRCS:.c=.o)
all: $(NAME)
$(NAME): $(OBJS)
$(AR) $(ARFLAGS) $(NAME) $(OBJS)
%.o: %.c libtci.h
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(OBJS)
fclean: clean
rm -f $(NAME)
re: fclean all
.PHONY: all clean fclean reThe SRCS line is empty for now. You will add a filename each time
you write a new .c file. The structure is the same Makefile you
built in f05/06, with two differences.
First: the target is libtci.a, not an executable. There is no
main. A library is a collection of compiled functions that other
programs link against.
Second: the link step uses ar (the archive tool) instead of gcc.
The rcs flags mean: replace or insert object files, create
the archive if it does not exist, and write an index (symbol
table) so the linker can find functions quickly. The result is a
.a file — a static archive. Static is explained on the library
page. For now, just know that running make will produce
libtci.a from your .c files.
The %.o: %.c libtci.h rule means every .c file depends on
libtci.h — a change to the header rebuilds everything.
Verify the Makefile works
Run make:
makeWith SRCS empty there is nothing to build, so ar creates an
empty archive:
ar rcs libtci.aThat is fine. The archive exists, it is empty, and make reports
success. The structure is in place. Move on to the first page.
GNU libc and BSD functions
On Linux, the C standard library is GNU libc — glibc. It implements
standard C (C99) and POSIX[1]. On macOS, the runtime is based on BSD
libc, which adds functions that are not part of POSIX: strlcpy,
strlcat, strnstr, and others.
This matters for libtci because several functions you will implement
— tci_strlcpy, tci_strlcat, tci_strnstr — are not available in
GNU libc on Linux. They will not be in <string.h> on your machine.
libtci includes them because they are genuinely useful and absent on
Linux: c01–c05 projects can use them without depending on a
third-party library.
When a function has this origin it is noted in its section. Standard C and POSIX functions work identically on Linux and macOS. BSD-origin functions in libtci are libtci's own implementations, not wrappers around anything the system provides on Linux.