Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions concepts/headers-includes/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"authors": [
"colinleach"
],
"contributors": [],
"blurb": "C uses header files (*.h) for various declarations. #include statements incorporate these declarations into the .c file."
}
92 changes: 92 additions & 0 deletions concepts/headers-includes/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# About

C requires that all functions are declared _before_ they are defined.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C requires functions to be declared before they are called or otherwise used in an expression. It's valid to define functions without declaring them first.
There is however the compiler option -Wmissing-declarations that warns about non-static(!) functions that are defined without being previously declared. The idea is that there are typically two kinds of functions, those declared in a .h file and meant to be called from other .c files, and local (static) helper functions that are effectively hidden from other translation units. The latter can be defined without a prior declaration and -Wmissing-declarations will not complain.

Did you want to write "called" instead of "defined"?

This can be done at the top of the `*.c` file containing the definition, but there are advantages in splitting declarations into a separate header file with a `.h` extension.

You can think of the header file as an API.
The header will tell you _what_ a codebase has to offer, without going into the details of _how_.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer "interface" instead of "API" because I believe it's easier to understand for people who don't know what an "API" is. But that's not a strong preference.
I believe these two sentences are the most important part of this document, the rest are technical details. I would love to see this part fleshed out.


## Header files

A header file can range from just a few lines to several hundred.

For a simple example, we can look at the `Hello World` exercise (which you already completed). This is the [`hello_world.h` file][hello-h] which is part of that exercise:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one line per sentence


```c
// This is called an include guard, which ensures that the header is only
// included once. You could alternatively use '#pragma once'. See
// https://en.wikipedia.org/wiki/Include_guard.
#ifndef HELLO_WORLD_H
#define HELLO_WORLD_H

// Declare the 'hello()' function, which takes no arguments and returns a
// 'const char *', i.e. a pointer to a character (in this case the first
// character in a string). The function itself is defined in the hello_world.c
// source file. This function is called by the test case(s) in the test source
// file test_hello_world.c.
const char *hello(void);

#endif
```

Most of it is explanatory comments (lines starting with `//`).

### Declarations
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this whole section. It talks about parameters but not about the name or return type.
If this is the main explanation of function declarations I would like it to be more extensive.
But is this really the place for that? I'd prefer a separate concept for functions that also covers function declarations.


Towards the bottom is the _declaration_ of the `hello()` function.
This is the same as in the _definition_ in `hello_world.c`, except that the declaration ends with a semicolon `;` instead of the function body.

This function takes no parameters (indicated by [`void`][void]).

Most functions have parameters, so these:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO "these" is ambiguous here. How about "their declarations"?


- _Must_ include the parameter type, and the compiler will enforce this.
- _Should_ also include the parameter name, as good style to make the header self-documenting.

For example:

```c
int remaining_minutes_in_oven(int actual_minutes_in_oven);
```

Many other things can be included in a header file, and you will see examples later in the syllabus.

### Guards
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer "Include Guards" instead of "Guard", like in the rest of this document.


Lines beginning with `#` are preprocessor directives, to ensure that the compiler only includes the header file once.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence can be interpreted as "Preprocessor directives ensure that the compiler only includes the header file once." But the preprocessor can do much more than that. How about splitting the sentence into two? The first part up to "directives" talks about preprocessor directives in general, and the second sentence explains the include guard.


- `HELLO_WORLD_H` is a constant, conventionally the file name converted to uppercase, with the dot replaced by underscore.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The official term is "identifier" not "constant".

- `#ifndef` is an abbreviation of "if not defined": only continue if the `HELLO_WORLD_H` constant does not already exist.
- `#define` creates the constant.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not happy with this sentence but right now I cannot think of an alternative.
Officially HELLO_WORLD_H is an "object-like macro" but that sounds too technical and too heavy for this introduction.

- `#endif` closes the `#ifndef` block.

## Includes

The [`hello_world.c` file][hello-c] starts with an [`#include`][include] as the first line.

```c
#include "hello_world.h"
```

That tells the compiler's preprocessor to insert the contents of `hello_world.h` at the top of `hello_world.c`, as though it had been typed there.
The whole expanded file is then compiled.

There are two variants of `#include`:

- Those in double-quotes, as above, tell the compiler to search the project directory for the header file.
- Those in brackets, such as `#include <math.h>`, are for items in the standard library. The compiler will search for these in a central location, such as `\usr\include` (on Ubuntu systems).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Officially both variants (double quotes and angle brackets) tell the the compiler to search for a header in two (potentially completely different) implementation-specific locations. One important difference is that if #include "h-char-sequence" is not supported or fails it falls back to #include <h-char-sequence>
In practice, #include "some_filename" first searches the local file and then behaves like #include <some_filename>.


Including system headers is very common in C code, even simple cases like an Exercism toy exercise.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Including system headers is very common in C code, even simple cases like an Exercism toy exercise.
Including system headers is very common in C code, even in simple cases like an Exercism toy exercise.


Common examples include `stdio.h` (_for printing, etc_), `string.h`, `math.h`, `stdlib.h` (_for various C99 standard library functions_).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest omitting "C99".


A typical *nix system probably has a few hundred system header files, available for you to use.

Future Concepts will note which imports are necessary to use the desired functions.

It may also be necessary to add a linker directive in the `makefile`, such as `LIBS = -lm` to use the math library, but that is a more advanced topic.

[hello-h]: https://github.com/exercism/c/blob/main/exercises/practice/hello-world/hello_world.h
[hello-c]: https://github.com/exercism/c/blob/main/exercises/practice/hello-world/hello_world.c
[void]: https://en.wikipedia.org/wiki/Void_type
[include]: https://en.wikipedia.org/wiki/Include_directive
84 changes: 84 additions & 0 deletions concepts/headers-includes/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Introduction

C requires that all functions are declared _before_ they are defined.
This can be done at the top of the `*.c` file containing the definition, but there are advantages in splitting declarations into a separate header file with a `.h` extension.

You can think of the header file as an API.
The header will tell you _what_ a codebase has to offer, without going into the details of _how_.

## Header files

A header file can range from just a few lines to several hundred.

For a simple example, we can look at the `Hello World` exercise (which you already completed). This is the `hello_world.h` file that is part of that exercise:

```c
// This is called an include guard, which ensures that the header is only
// included once. You could alternatively use '#pragma once'. See
// https://en.wikipedia.org/wiki/Include_guard.
#ifndef HELLO_WORLD_H
#define HELLO_WORLD_H

// Declare the 'hello()' function, which takes no arguments and returns a
// 'const char *', i.e. a pointer to a character (in this case the first
// character in a string). The function itself is defined in the hello_world.c
// source file. This function is called by the test case(s) in the test source
// file test_hello_world.c.
const char *hello(void);

#endif
```

Most of it is explanatory comments (lines starting with `//`).

### Declarations

Towards the bottom is the _declaration_ of the `hello()` function.
This is the same as in the _definition_ in `hello_world.c`, except that the declaration ends with a semicolon `;` instead of the function body.

This function takes no parameters (indicated by `void`).

Most functions have parameters, so these:

- _Must_ include the parameter type, and the compiler will enforce this.
- _Should_ also include the parameter name, as good style to make the header self-documenting.

For example:

```c
int remaining_minutes_in_oven(int actual_minutes_in_oven);
```

Many other things can be included in a header file, and you will see examples later in the syllabus.

### Guards

Lines beginning with `#` are to ensure that the compiler only includes the header file once.

- `HELLO_WORLD_H` is a constant, conventionally the file name converted to uppercase, with the dot replaced by underscore.
- `#ifndef` is an abbreviation of "if not defined": only continue if the `HELLO_WORLD_H` constant does not already exist.
- `#define` creates the constant.
- `#endif` closes the `#ifndef` block.

## Includes

The `hello_world.c` file starts with an `#include` as the first line.

```c
#include "hello_world.h"
```

That tells the compiler's preprocessor to insert the contents of `hello_world.h` at the top of `hello_world.c`, as though it had been typed there.
The whole expanded file is then compied.

There are two variants of `#include`:

- Those in double-quotes, as above, tell the compiler to search the project directory for the header file.
- Those in brackets, such as `#include <math.h>`, are for items in the standard library. The compiler will search for these in
a central location, such as `\usr\include` (on Ubuntu systems).

Including system headers is very common in C code, even simple cases like an Exercism toy exercise.

Common examples include `stdio.h` (_for printing, etc_), `string.h`, `math.h`, `stdlib.h` (_for various C99 standard library functions_).

Future Concepts will note which imports are necessary to use the desired functions.
6 changes: 6 additions & 0 deletions concepts/headers-includes/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"url": "https://en.wikipedia.org/wiki/Include_directive",
"description": "Include directive (Wikipedia)"
}
]
5 changes: 5 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1180,6 +1180,11 @@
"slug": "basics",
"name": "Basics"
},
{
"uuid": "95d7085c-69fd-4799-ae2a-bdc9306da436",
"slug": "headers-includes",
"name": "Headers and Includes"
},
{
"uuid": "20948c69-20f7-498d-b743-fada00e5c79d",
"slug": "bits",
Expand Down