-
-
Notifications
You must be signed in to change notification settings - Fork 208
Draft concept: Headers and Includes #1084
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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." | ||
| } |
| 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. | ||||||
| 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_. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||||||
|
|
||||||
| ## 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: | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||||||
|
|
||||||
| 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: | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||||||
| - `#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). | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||||||
|
|
||||||
| Including system headers is very common in C code, even simple cases like an Exercism toy exercise. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| Common examples include `stdio.h` (_for printing, etc_), `string.h`, `math.h`, `stdlib.h` (_for various C99 standard library functions_). | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||||||
| 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. |
| 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)" | ||
| } | ||
| ] |
There was a problem hiding this comment.
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-declarationsthat 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.hfile and meant to be called from other.cfiles, and local (static) helper functions that are effectively hidden from other translation units. The latter can be defined without a prior declaration and-Wmissing-declarationswill not complain.Did you want to write "called" instead of "defined"?