Compiling Workflow

  1. Lexical & Syntax Analysis: (Frontend) Tokenize source code into abstract syntax tree
  2. Semantic Analysis: (Middle-end) Optimize AST
  3. Code Generation: (Backend) Generate assembly from AST
  4. Assemble: Convert assembly to machine code object files (.o)
  5. Link All Files: Combine multiple object files to one executable file (previous steps work with one file only)

Headers

Header Files: (.h) Declarations of functions, types shared by multiple sources, implemented somewhere else

// Guard condition
#ifndef THIS_HEADER_H
#define THIS_HEADER_H

// Define types
typedef struct(char *color) Cat;

// Define functions
void catsay(Cat *cat);

// Define varaibles (Setting values for variables in headers would fail on linking)
extern const int CONSTANT;
extern int array[];

#endif // THIS_HEADER_H

Linking Quirks

// Visible in all sources during linking, might produce a variable conflict
int x = 0;

// Static in global scope: Make variable visible in only the current file
static int y = 0;

void function() {
	// Static in local scope: Make variable preserve value through multiple function calls
	static int z = 0;
	z++;
}

Build Tool: Make

Target: Contain dependencies and recipe

Dependency: Specify another target to compile before this target

Recipe: The command to produce a specific target

# Variables
CFLAGS = -Wall -Werror -g -std=gnu99

# Targets
# target: dependencies
#		recipe
friendme: friendme.o friends.o
	gcc $(CFLAGS) -o friendme friendme.o friends.o

friendme.o: friendme.c friends.h
	gcc $(CFLAGS) -c friendme.c

friends.o: friends.c friends.h
	gcc $(CFLAGS) -c friends.c

# Wildcards
%.o: %.c friend.h
	gcc $(CFLAGS) -c $< -o $@

# Pseudo-target
.PHONY: clean
clean: 
	rm friendme *.o

Make only re-compile changed file

Preprocessor

Define in compiler: gcc -D MACRO=1 ...

// Always wrap macros in parenthesis, don't add semicolon
#define CONSTANT (32)
#define FUNCTION(arg1, arg2) (arg1 + arg2) 

#ifdef __APPLE__
// do something
#elif defined(__gnu_linux__)
// do something else
#else
#endif

// Multi-line functions: Use a do-while wrapper to ensure the macro is used as a statement
#define MULTILINE(arg) \\
	do { \\
		printf(arg); \\
	} while (0)