Module 8.2

Header Files and Modular Design

Learn to organize your C code into reusable modules using header files. Master the art of separating declarations from definitions, creating robust include guards, and building professional multi-file projects that are easy to maintain and scale.

35 min read
Intermediate
Hands-on Examples
What You'll Learn
  • What belongs in header files
  • Creating custom header files
  • Include guards and #pragma once
  • Declarations vs definitions
  • Multi-file project organization
Contents
01

What Are Header Files?

Header files are the glue that holds multi-file C programs together. They contain declarations that tell the compiler about functions, types, and variables defined elsewhere, enabling different parts of your program to communicate without knowing implementation details.

The Purpose of Header Files

Imagine you are writing a large program with thousands of lines of code. Putting everything in one file would be a nightmare to navigate, debug, and maintain. Header files solve this by acting as contracts or interfaces between different parts of your program.

When you write #include <stdio.h>, you are including a header file that declares functions like printf() and scanf(). The actual code for these functions lives in a library - the header just tells the compiler what the functions look like so it can check your calls are correct.

File Type

Header File (.h)

A header file is a file with a .h extension that contains declarations (not definitions) meant to be shared across multiple source files. It serves as an interface, telling the compiler about types, functions, and variables without providing the actual implementation.

Key principle: Headers declare, source files define. A header says "this function exists and takes these parameters" while the .c file provides the actual code.

What Belongs in Header Files

Put in Headers
  • Function prototypes (declarations)
  • Type definitions (struct, enum, typedef)
  • Macro definitions (#define)
  • External variable declarations (extern)
  • Include guards
  • Other #include directives needed
Keep Out of Headers
  • Function definitions (implementations)
  • Variable definitions (allocations)
  • Static functions (file-local)
  • Large amounts of code
  • Executable statements
  • Local variables

Standard Library Headers

You have been using standard headers throughout this course. Here are some common ones and what they provide:

Header Purpose Key Declarations
<stdio.h> Standard I/O printf, scanf, FILE, fopen, fclose
<stdlib.h> General utilities malloc, free, exit, atoi, rand
<string.h> String operations strlen, strcpy, strcmp, memcpy
<math.h> Math functions sqrt, pow, sin, cos, log
<stdbool.h> Boolean type bool, true, false
<stdint.h> Fixed-width integers int32_t, uint8_t, INT_MAX

How Headers Work

When the preprocessor encounters #include, it literally copies the contents of the header file into your source file. This is pure text substitution - there is nothing magical about it.

// Before preprocessing
#include <stdio.h>

int main() {
    printf("Hello\n");
    return 0;
}

// After preprocessing (simplified)
// ... thousands of lines from stdio.h ...
int printf(const char *format, ...);
// ... more declarations ...

int main() {
    printf("Hello\n");
    return 0;
}
Why does this matter? Understanding that #include is just text substitution explains why include guards are necessary (to prevent duplicate declarations) and why you should not put definitions in headers (they would be duplicated in every file that includes them).
Practice Questions

Task: Which of these should go in a header file? Mark each as Header or Source.

1. int add(int a, int b);
2. int add(int a, int b) { return a + b; }
3. typedef struct { int x, y; } Point;
4. #define MAX_SIZE 100
5. int counter = 0;
6. extern int globalCount;
Show Solution
  1. int add(int a, int b); - Header (function declaration/prototype)
  2. int add(...) { return a + b; } - Source (function definition)
  3. typedef struct... - Header (type definition)
  4. #define MAX_SIZE 100 - Header (macro definition)
  5. int counter = 0; - Source (variable definition with allocation)
  6. extern int globalCount; - Header (external variable declaration)

Task: Which header file do you need to include for each function?

1. strlen()
2. malloc()
3. sqrt()
4. printf()
5. isalpha()
Show Solution
#include <string.h>   // 1. strlen()
#include <stdlib.h>   // 2. malloc()
#include <math.h>     // 3. sqrt()
#include <stdio.h>    // 4. printf()
#include <ctype.h>    // 5. isalpha()
02

Creating Custom Header Files

Creating your own header files is essential for organizing larger programs. A well-designed header file provides a clean interface to your module, hiding implementation details while exposing only what other parts of the program need to know.

Basic Header File Structure

Every header file should follow a consistent structure: include guards at the top, necessary includes, type definitions, macro definitions, and finally function prototypes. This ordering ensures dependencies are resolved correctly.

// math_utils.h - A custom header file

#ifndef MATH_UTILS_H    // Include guard start
#define MATH_UTILS_H

// Include other headers this header depends on
#include <stdbool.h>

// Macro definitions
#define PI 3.14159265359
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))

// Type definitions
typedef struct {
    double x;
    double y;
} Point;

typedef struct {
    Point center;
    double radius;
} Circle;

// Function prototypes (declarations only!)
double distance(Point a, Point b);
double circle_area(Circle c);
double circle_circumference(Circle c);
bool point_in_circle(Point p, Circle c);

#endif  // MATH_UTILS_H - Include guard end

The Corresponding Source File

The source file (.c) includes its own header and provides the actual implementations:

// math_utils.c - Implementation file

#include "math_utils.h"  // Include own header first
#include <math.h>        // For sqrt()

// Function definitions (implementations)
double distance(Point a, Point b) {
    double dx = b.x - a.x;
    double dy = b.y - a.y;
    return sqrt(dx * dx + dy * dy);
}

double circle_area(Circle c) {
    return PI * c.radius * c.radius;
}

double circle_circumference(Circle c) {
    return 2 * PI * c.radius;
}

bool point_in_circle(Point p, Circle c) {
    return distance(p, c.center) <= c.radius;
}

Using Your Custom Header

Now any file can use your math utilities by including the header:

// main.c - Uses the math_utils module

#include <stdio.h>
#include "math_utils.h"  // Your custom header

int main() {
    Point p1 = {0.0, 0.0};
    Point p2 = {3.0, 4.0};
    
    printf("Distance: %.2f\n", distance(p1, p2));  // 5.00
    
    Circle c = {{0.0, 0.0}, 5.0};
    printf("Area: %.2f\n", circle_area(c));  // 78.54
    
    if (point_in_circle(p2, c)) {
        printf("Point is inside the circle\n");
    }
    
    return 0;
}

Header File Naming Conventions

Convention Example When to Use
Module name database.h General purpose modules
Feature prefix str_utils.h Utility libraries
Project prefix myapp_config.h Project-specific headers
Internal marker internal.h Implementation details
Best Practice: Self-Contained Headers

A header should include everything it needs to compile on its own. If your header uses bool, include <stdbool.h>. If it uses size_t, include <stddef.h>. Users should not need to know what other headers to include first.

Practice Questions

Task: Create a header file temperature.h with functions to convert between Celsius and Fahrenheit.

Show Solution
// temperature.h
#ifndef TEMPERATURE_H
#define TEMPERATURE_H

// Convert Celsius to Fahrenheit
double celsius_to_fahrenheit(double celsius);

// Convert Fahrenheit to Celsius
double fahrenheit_to_celsius(double fahrenheit);

// Check if temperature is freezing (Celsius)
int is_freezing(double celsius);

#endif  // TEMPERATURE_H

Task: Create a header student.h with a Student struct and functions to create, print, and compare students.

Show Solution
// student.h
#ifndef STUDENT_H
#define STUDENT_H

#include <stdbool.h>

#define MAX_NAME_LENGTH 50

typedef struct {
    int id;
    char name[MAX_NAME_LENGTH];
    float gpa;
} Student;

// Create a new student
Student create_student(int id, const char *name, float gpa);

// Print student information
void print_student(const Student *s);

// Compare two students by GPA (returns true if a has higher GPA)
bool compare_gpa(const Student *a, const Student *b);

// Check if student is on honor roll (GPA >= 3.5)
bool is_honor_roll(const Student *s);

#endif  // STUDENT_H

Task: Create both header and source files for a simple stack data structure.

Show Solution
// stack.h
#ifndef STACK_H
#define STACK_H

#include <stdbool.h>

#define STACK_CAPACITY 100

typedef struct {
    int items[STACK_CAPACITY];
    int top;
} Stack;

// Initialize an empty stack
void stack_init(Stack *s);

// Push an item onto the stack
bool stack_push(Stack *s, int value);

// Pop an item from the stack
bool stack_pop(Stack *s, int *value);

// Peek at the top item without removing it
bool stack_peek(const Stack *s, int *value);

// Check if stack is empty
bool stack_is_empty(const Stack *s);

// Check if stack is full
bool stack_is_full(const Stack *s);

// Get current size of stack
int stack_size(const Stack *s);

#endif  // STACK_H

// ========================================
// stack.c
#include "stack.h"

void stack_init(Stack *s) {
    s->top = -1;
}

bool stack_push(Stack *s, int value) {
    if (stack_is_full(s)) return false;
    s->items[++s->top] = value;
    return true;
}

bool stack_pop(Stack *s, int *value) {
    if (stack_is_empty(s)) return false;
    *value = s->items[s->top--];
    return true;
}

bool stack_peek(const Stack *s, int *value) {
    if (stack_is_empty(s)) return false;
    *value = s->items[s->top];
    return true;
}

bool stack_is_empty(const Stack *s) {
    return s->top == -1;
}

bool stack_is_full(const Stack *s) {
    return s->top == STACK_CAPACITY - 1;
}

int stack_size(const Stack *s) {
    return s->top + 1;
}
03

Include Guards and #pragma once

Include guards are essential for preventing multiple inclusion of header files, which would cause redefinition errors. Every header file you create should have include guards - no exceptions.

The Multiple Inclusion Problem

Without include guards, including the same header twice (directly or indirectly) causes errors:

// point.h (NO include guards - BAD!)
typedef struct {
    int x, y;
} Point;

// graphics.h
#include "point.h"  // Includes Point definition
typedef struct {
    Point start, end;
} Line;

// main.c
#include "point.h"     // Point defined here
#include "graphics.h"  // Includes point.h again!
// ERROR: redefinition of 'Point'

Traditional Include Guards

Include guards use preprocessor conditionals to ensure header contents are processed only once:

// point.h (WITH include guards - GOOD!)
#ifndef POINT_H       // If POINT_H is NOT defined...
#define POINT_H       // ...define it

typedef struct {
    int x, y;
} Point;

Point create_point(int x, int y);
double distance(Point a, Point b);

#endif  // POINT_H    // End of conditional

// Now including point.h multiple times is safe:
// First include: POINT_H undefined, contents processed, POINT_H defined
// Second include: POINT_H already defined, contents skipped

Naming Conventions for Include Guards

Style Example Notes
Simple uppercase FILENAME_H Most common, simple
With underscores _FILENAME_H_ Avoid - reserved for implementation
Project prefix MYPROJECT_MODULE_H Prevents conflicts across projects
Path-based SRC_UTILS_STRING_H Includes directory structure
UUID-based H_5A8F3... Guaranteed unique, less readable

#pragma once Alternative

Modern compilers support #pragma once as a simpler alternative to include guards:

// point.h using #pragma once
#pragma once

typedef struct {
    int x, y;
} Point;

Point create_point(int x, int y);
double distance(Point a, Point b);
#pragma once Pros
  • Simpler - just one line
  • No need to invent unique names
  • Cannot have typos in guard name
  • Faster compilation (sometimes)
#pragma once Cons
  • Not in C standard (compiler extension)
  • May fail with symbolic links
  • Not supported by all compilers
  • Behavior can vary across platforms

Belt-and-Suspenders Approach

For maximum compatibility, you can use both methods:

// point.h - Using both methods
#ifndef POINT_H
#define POINT_H

#pragma once  // Compiler uses whichever it prefers

typedef struct {
    int x, y;
} Point;

#endif  // POINT_H
Common Mistake: Mismatched Guards

Always ensure your #ifndef, #define, and #endif use the exact same macro name. A typo like #ifndef POINT_H followed by #define PONIT_H will not work!

Practice Questions

Task: Add proper include guards to this header file:

// config.h
#define VERSION "1.0.0"
#define MAX_USERS 100
#define DEBUG_MODE 1
Show Solution
// config.h
#ifndef CONFIG_H
#define CONFIG_H

#define VERSION "1.0.0"
#define MAX_USERS 100
#define DEBUG_MODE 1

#endif  // CONFIG_H

Task: Find and fix the bug in these include guards:

// utils.h
#ifndef UTILS_H
#define UTIL_H

int max(int a, int b);
int min(int a, int b);

#endif
Show Solution
// utils.h - Fixed version
#ifndef UTILS_H
#define UTILS_H    // Fixed: was UTIL_H, now UTILS_H

int max(int a, int b);
int min(int a, int b);

#endif  // UTILS_H

// The bug: #ifndef checks UTILS_H but #define sets UTIL_H (missing 'S')
// This means the guard never works - the header can be included multiple times
04

Declarations vs Definitions

Understanding the difference between declarations and definitions is crucial for proper header file design. Confusing them leads to linker errors that can be difficult to debug.

The Key Difference

Concept

Declaration vs Definition

A declaration introduces a name and tells the compiler about its type, but does not allocate storage or provide implementation. A definition creates the actual entity - it allocates memory for variables or provides code for functions.

Rule of thumb: You can declare something multiple times (in multiple files), but you can only define it once across the entire program. This is called the One Definition Rule (ODR).

Function Declarations and Definitions

// DECLARATION (prototype) - goes in header
int add(int a, int b);           // No body - just the signature
int multiply(int, int);          // Parameter names optional in declaration

// DEFINITION (implementation) - goes in source file
int add(int a, int b) {          // Has body with actual code
    return a + b;
}

int multiply(int x, int y) {
    return x * y;
}

Variable Declarations and Definitions

// DEFINITION - creates the variable (allocates memory)
int counter = 0;                 // Definition with initialization
double pi = 3.14159;             // Definition
int values[100];                 // Definition - allocates array

// DECLARATION - tells compiler about variable defined elsewhere
extern int counter;              // Declaration only (extern keyword)
extern double pi;                // Declaration only
extern int values[];             // Declaration (size optional)

// In practice:
// config.c
int globalConfig = 42;           // Definition

// config.h  
extern int globalConfig;         // Declaration - so other files can use it

Type Declarations and Definitions

// DECLARATION - incomplete type (forward declaration)
struct Node;                     // Declares Node exists, no details
typedef struct Node Node;        // Can create typedef from declaration

// DEFINITION - complete type
struct Node {                    // Defines the actual structure
    int data;
    struct Node *next;
};

// Type definitions can appear in headers because they don't allocate memory
// They just tell the compiler how types are structured

Summary Table

Element Declaration (Header) Definition (Source)
Function int func(int x); int func(int x) { return x*2; }
Variable extern int count; int count = 0;
Struct struct Data; struct Data { int x; };
Typedef typedef int MyInt; (same in both - no memory allocated)
Macro #define MAX 100 (same in both - preprocessor)

The extern Keyword

The extern keyword is how you declare a variable that is defined elsewhere:

// globals.c - The ONE definition
int errorCount = 0;
char programName[256] = "MyApp";

// globals.h - Declarations for other files to use
#ifndef GLOBALS_H
#define GLOBALS_H

extern int errorCount;           // "There's an int errorCount somewhere"
extern char programName[];       // "There's a char array somewhere"

#endif

// main.c - Using the global variables
#include "globals.h"

void logError(const char *msg) {
    errorCount++;                // Uses the variable defined in globals.c
    printf("[%s] Error #%d: %s\n", programName, errorCount, msg);
}
Why does this matter? If you put a definition (not declaration) in a header, every file that includes it will have its own copy. The linker will then complain about multiple definitions of the same symbol.
Practice Questions

Task: Mark each line as Declaration (D) or Definition (Def):

1. int calculate(int x);
2. int calculate(int x) { return x * 2; }
3. extern double PI;
4. double PI = 3.14159;
5. typedef int Integer;
6. struct Person { char name[50]; int age; };
Show Solution
  1. int calculate(int x); - Declaration (no body)
  2. int calculate(int x) { return x * 2; } - Definition (has body)
  3. extern double PI; - Declaration (extern = declaration)
  4. double PI = 3.14159; - Definition (allocates memory)
  5. typedef int Integer; - Definition (type alias definition)
  6. struct Person {...}; - Definition (complete struct definition)

Task: This code causes "multiple definition" linker errors. Fix it:

// counter.h
#ifndef COUNTER_H
#define COUNTER_H

int count = 0;  // Problem!

void increment(void);
int get_count(void);

#endif
Show Solution
// counter.h - Fixed
#ifndef COUNTER_H
#define COUNTER_H

extern int count;  // Declaration only

void increment(void);
int get_count(void);

#endif

// counter.c - Add definition here
#include "counter.h"

int count = 0;  // Definition in ONE source file

void increment(void) {
    count++;
}

int get_count(void) {
    return count;
}

Task: These two structs need to reference each other. Use forward declarations to make it work:

// person.h - Person has a pointer to their Company
// company.h - Company has a pointer to its CEO (Person)
// Currently causes circular dependency!
Show Solution
// person.h
#ifndef PERSON_H
#define PERSON_H

struct Company;  // Forward declaration

typedef struct Person {
    char name[50];
    struct Company *employer;  // Can use pointer to incomplete type
} Person;

Person *create_person(const char *name);

#endif

// company.h
#ifndef COMPANY_H
#define COMPANY_H

struct Person;  // Forward declaration

typedef struct Company {
    char name[100];
    struct Person *ceo;  // Can use pointer to incomplete type
    int employee_count;
} Company;

Company *create_company(const char *name);

#endif

// Note: Forward declarations work for pointers because all pointers
// are the same size. You cannot use forward declarations where
// the compiler needs to know the struct's size.
05

Multi-File Project Organization

As your C projects grow, proper organization becomes critical. A well-structured project is easier to understand, maintain, debug, and extend. Let us look at how to organize files and compile multi-file projects.

Typical Project Structure

my_project/
├── include/           # Header files
│   ├── math_utils.h
│   ├── string_utils.h
│   └── data_types.h
├── src/               # Source files
│   ├── main.c
│   ├── math_utils.c
│   └── string_utils.c
├── tests/             # Test files
│   └── test_math.c
├── build/             # Compiled objects (generated)
├── bin/               # Executables (generated)
├── Makefile           # Build configuration
└── README.md          # Documentation

Example: Calculator Project

Let us build a simple calculator with proper modular design:

// include/calculator.h
#ifndef CALCULATOR_H
#define CALCULATOR_H

// Basic operations
double add(double a, double b);
double subtract(double a, double b);
double multiply(double a, double b);
double divide(double a, double b, int *error);

// Advanced operations
double power(double base, int exponent);
double square_root(double x, int *error);

// Error codes
#define CALC_OK 0
#define CALC_DIV_BY_ZERO 1
#define CALC_NEGATIVE_SQRT 2

#endif  // CALCULATOR_H
// src/calculator.c
#include "calculator.h"
#include <math.h>

double add(double a, double b) {
    return a + b;
}

double subtract(double a, double b) {
    return a - b;
}

double multiply(double a, double b) {
    return a * b;
}

double divide(double a, double b, int *error) {
    if (b == 0.0) {
        *error = CALC_DIV_BY_ZERO;
        return 0.0;
    }
    *error = CALC_OK;
    return a / b;
}

double power(double base, int exponent) {
    return pow(base, exponent);
}

double square_root(double x, int *error) {
    if (x < 0) {
        *error = CALC_NEGATIVE_SQRT;
        return 0.0;
    }
    *error = CALC_OK;
    return sqrt(x);
}
// src/main.c
#include <stdio.h>
#include "calculator.h"

int main() {
    int error;
    
    printf("5 + 3 = %.2f\n", add(5, 3));           // 8.00
    printf("10 - 4 = %.2f\n", subtract(10, 4));   // 6.00
    printf("6 * 7 = %.2f\n", multiply(6, 7));     // 42.00
    
    double result = divide(10, 2, &error);
    if (error == CALC_OK) {
        printf("10 / 2 = %.2f\n", result);        // 5.00
    }
    
    result = divide(10, 0, &error);
    if (error == CALC_DIV_BY_ZERO) {
        printf("Error: Division by zero!\n");
    }
    
    printf("2^10 = %.2f\n", power(2, 10));        // 1024.00
    
    return 0;
}

Compiling Multi-File Projects

# Method 1: Compile all at once
gcc -I include src/main.c src/calculator.c -o calculator -lm

# Method 2: Compile separately (better for large projects)
gcc -I include -c src/calculator.c -o build/calculator.o
gcc -I include -c src/main.c -o build/main.o
gcc build/calculator.o build/main.o -o bin/calculator -lm

# The -I flag adds include/ to the header search path
# The -c flag compiles without linking (creates .o object file)
# The -lm links the math library (needed for sqrt, pow)

Using a Makefile

Makefiles automate the build process and only recompile changed files:

# Makefile
CC = gcc
CFLAGS = -Wall -Wextra -I include
LDFLAGS = -lm

SRCDIR = src
BUILDDIR = build
BINDIR = bin

SOURCES = $(wildcard $(SRCDIR)/*.c)
OBJECTS = $(patsubst $(SRCDIR)/%.c,$(BUILDDIR)/%.o,$(SOURCES))
TARGET = $(BINDIR)/calculator

all: $(TARGET)

$(TARGET): $(OBJECTS) | $(BINDIR)
	$(CC) $(OBJECTS) -o $@ $(LDFLAGS)

$(BUILDDIR)/%.o: $(SRCDIR)/%.c | $(BUILDDIR)
	$(CC) $(CFLAGS) -c $< -o $@

$(BUILDDIR) $(BINDIR):
	mkdir -p $@

clean:
	rm -rf $(BUILDDIR) $(BINDIR)

.PHONY: all clean

Include Order Best Practices

// Recommended include order in source files:

// 1. Own header first (catches missing dependencies)
#include "mymodule.h"

// 2. System headers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 3. Third-party library headers
#include <sqlite3.h>
#include <curl/curl.h>

// 4. Other project headers
#include "utils.h"
#include "config.h"
Why Own Header First?

Including your own header first ensures it is self-contained. If mymodule.h needs <stdlib.h> but forgot to include it, you will get an error immediately rather than having it accidentally work because some other file included stdlib first.

Practice Questions

Task: Write the gcc command to compile these files:

project/
├── include/
│   └── utils.h
├── src/
│   ├── main.c
│   └── utils.c
Show Solution
# All at once:
gcc -I include src/main.c src/utils.c -o program

# Or separately:
gcc -I include -c src/utils.c -o utils.o
gcc -I include -c src/main.c -o main.o
gcc utils.o main.o -o program

Task: Design header and source file structure for a simple bank account module with: create account, deposit, withdraw, get balance.

Show Solution
// account.h
#ifndef ACCOUNT_H
#define ACCOUNT_H

#include <stdbool.h>

typedef struct {
    int id;
    char holder_name[100];
    double balance;
} Account;

// Create a new account
Account create_account(int id, const char *name, double initial_balance);

// Deposit money (returns true if successful)
bool deposit(Account *acc, double amount);

// Withdraw money (returns true if successful)
bool withdraw(Account *acc, double amount);

// Get current balance
double get_balance(const Account *acc);

// Print account info
void print_account(const Account *acc);

#endif  // ACCOUNT_H

// account.c
#include "account.h"
#include <stdio.h>
#include <string.h>

Account create_account(int id, const char *name, double initial) {
    Account acc;
    acc.id = id;
    strncpy(acc.holder_name, name, 99);
    acc.holder_name[99] = '\0';
    acc.balance = initial > 0 ? initial : 0;
    return acc;
}

bool deposit(Account *acc, double amount) {
    if (amount <= 0) return false;
    acc->balance += amount;
    return true;
}

bool withdraw(Account *acc, double amount) {
    if (amount <= 0 || amount > acc->balance) return false;
    acc->balance -= amount;
    return true;
}

double get_balance(const Account *acc) {
    return acc->balance;
}

void print_account(const Account *acc) {
    printf("Account #%d: %s, Balance: $%.2f\n",
           acc->id, acc->holder_name, acc->balance);
}

Key Takeaways

Headers Are Interfaces

Headers declare what a module provides; source files contain the implementation

Always Use Include Guards

#ifndef/#define/#endif or #pragma once prevents multiple inclusion errors

Declarations vs Definitions

Declare in headers (can repeat), define in source files (only once)

Use extern for Variables

extern declares variables defined elsewhere; define in one .c file only

Organize Large Projects

Separate include/, src/, build/ directories for clean project structure

Self-Contained Headers

Headers should include everything they need - do not rely on include order

Knowledge Check

Quick Quiz

Test what you have learned about C header files and modular design

1 What is the primary purpose of a header file?
2 What do include guards prevent?
3 Which of these should NOT go in a header file?
4 What does the extern keyword do?
5 What is wrong with #ifndef MYFILE_H followed by #define MY_FILE_H?
6 Why should a source file include its own header first?
Answer all questions to check your score