Note 06: Macros, Header Files, Debugging - [Homepage](http://gddh.github.io/)
=======================
# Preprocessor
## Macros
[_macro_](https://gcc.gnu.org/onlinedocs/cpp/Macros.html#Macros) gives a name to a piece of code. Before compilation, the preprocessor will replace that name with the piece of code that it defines.
### Ex1:
```
#define BUF_SIZE 1024
```
The preprocessor will replace ```BUF_SIZE``` with 1024 in the code.
```
str = (char *)malloc(sizeof(char) * BUF_SIZE);
|
| Preprocessor will do this.
V
str = (char *)malloc(sizeof(char) * 1024);
```
### Ex2:
```
#define GOLDEN_R_DEC (1.618033 - 1)
#define BAD_GR_DEC 1.618033 - 1
```
```
printf("%f\n", GOLDEN_R_DEC * 2); --> 1.236066
printf("%f\n", BAD_GR_DEC * 2); --> -0.381967
```
### Ex3:
```
# define NUMS 1, \
2, \
3
```
```
void print_int_arr(int *p)
{
int i;
i = 0;
while (i < 3)
{
printf("%d ", p[i]);
i = i + 1;
}
printf("\n");
}
int a[] = {NUMS};
print_int_arr(a); ---> 1 2 3
```
### Ex 4:
```
# define max(A, B) (A > B ? A : B)
```
```
printf("%d\n", max(3, 4)); ---> 4
```
### Summary:
- Always keep in mind that the macro will paste that exact piece of code into its name
- Easy to change
- Ex1: If we decide we want to use another buffer size, we just need to change the define statement.
- Readability
- Ex4 It is much easier to read max
- Be careful
- when defining functions, using parenthesis, mathematical precedence, and long definitions. The online doc has a [section](https://gcc.gnu.org/onlinedocs/gcc-3.0.1/cpp_3.html#SEC26) just for pitfalls of macros.
- Type neutral
- There is no actual function call even though it seems like it.
- Ex 04: max is not an actual function. We are replacing the ```max``` with ```(A > B ? A : B)```
## Conditional Compilation
We can control the compilation of our program using conditional compilation commands. These commands will delimit which parts of our code will be compiled based on some condition. The preprocessor will evaluate the conditions and remove the parts that will not be compiled before giving it to the compiler. We will focus on three commands, ```ifndef```, ```endif```, ```ifdef```.
### ```endif```
This command marks the terminates `#if` , `#ifdef` , `#ifndef` , `#else` , or `#elif`.
### ```ifdef```
```
#ifdef MACRO
If MACRO is defined, then the preprocessor will not take this section out.
This section is then passed to the compiler.
#endif /* MACRO */
```
### ```ifndef```
```
#ifndef MACRO
If MACRO is defined, then the preprocessor will take this section out.
This section will only be kept if it is not defined.
This section is not passed to the compiler if MACRO is defined.
#endif /* MACRO */
```
**Note:**
- It is possible to nest conditionals.
- The text comments after ```#endif``` are optional. When using lots of conditional compilation, it is a good idea because its easier to identify where each conditional ends.
### Include Guards
The conditionals make it easy to ensure that any given file is compiled once even though that file may be included in many different places. We see how this is used in header files.
```
#ifndef FILE_H
#define FILE_H
/* code */
#endif /* FILE_H */
```
The include guard will check if ```FILE_H``` has already been defined.
- If it has been defined, then the preprocessor will ignore the header file because its already been taken care of.
- If it hasn't been defined, then the header file will be defined by ```#define FILE_H```. (```FILE_H``` is defined as a macro)
- The preprocessor will include the code between the ifndef and the endif when handing the preprocessed code to the compiler.
## Header Files
As we continue onto building more sophisticated programs, we will find that Norm and really the manageability of your program will compel you to split your programs into different files. H files become essential in managing sophisticated programs.
The following example shows us what header files can do for us. Let's first consider two files: ```ft_put.c```, ```main.c```
**ft_put.c**
```
#include <unistd.h>
void ft_putchar(char c)
{
write(1, &c, 1);
}
void ft_putstr(char *str)
{
int i;
i = 0;
while (str[i] != '\0')
{
ft_putchar(str[i]);
i = i + 1;
}
}
```
**main.c**
```
void ft_putstr(char *str);
int main(int argc, char **argv)
{
ft_putstr("Hello World!\n");
}
```
Now when we compile
```
$> gcc -Wall -Wextra -Werror main.c ft_put.c
$> ./a.out
Hello World
```
Currently ```main.c``` uses ```ft_putstr``` and to do so, ```main.c``` needs to declare ```ft_putstr```. If ```main.c``` needed ```ft_putchar```, then ```main.c``` would also need to declare ```ft_putchar``` as follows
**main.c - if we need ft_putchar**
```
void ft_putstr(char *str);
void ft_putchar(char c);
int main(int argc, char **argv)
{
ft_putstr("Hello World!");
ft_putchar('\n');
}
```
This may seem fine, but if we have multiple files that are using ```ft_putchar``` and ```ft_putstr```, we don't want to declare the functions in each file. (For further clarifications as to why, review Note 02.)
Header files provide an elegant solution. Let's see how we use them. (We don't need to change ft_put.c)
**test.h**
```
#ifndef TEST_H
# define TEST_H
void ft_putchar(char c);
void ft_putstr(char *str);
# endif
```
**main.c - include header**
```
#include "test.h" <-- copies the content for us
int main(int argc, char **argv)
{
ft_putstr("Hello World!\n");
}
```
Compilation and the result will be the same. Now let's say foo.c also needs ```ft_putstr``` and ```ft_putchar```. Instead of declaring the two functions in foo.c, all we need to do in foo.c is ```#include "test.h"```.
# Debugging Tips
**Disclaimer:** I'm simply sharing how I *usually* go about debugging programs. These beliefs are **current** to when I'm writing this, and I have much to learn, so without a doubt I'll be improving and trying new things.
If you find better ways, please teach me! Without doubt, your experiences may allow you to see things I haven't so I'd be happy to discuss your practices.
## Proactive Approach
### Code by writing
I find when I'm writing out my code, I am more likely to have a clear, detailed plan. I can clearly go through the process and run simple simulations before going to my keyboard.
I do this for functions that are a bit more complicated than the usual. Usually these are functions where I am pretty sure I will make a mistake in.
I do this by writing out the tasks, then writing pseudocode for each task, then "refractoring" the pseudocode until I'm comfortable going to the keyboard.
### Test Driven Development
[TDD](https://en.wikipedia.org/wiki/Test-driven_development) is a development process where:
1. write the tests for an expected behavior,
2. run the tests to ensure that they fail
3. write code
4. run tests (if they fail, start from 3 again)
5. Refactor code
This is a pretty robust development process. The idea is to keep the tests simple and easy and write code to match basic functionalities to make sure things are working.
I personally,
- don't build all out tests because as I'm developing I just find it too time consuming.
- Sometimes comes back to bite me.
- I find it draining and I sometimes lose focus on bigger picture when I do
- find myself doing this for bigger/unfamiliar programs
- I break down the problems into subproblems that naturally present simple tests
- for tougher tasks I find myself in a cycle of positive reinforcement
- I can see I am making progress
- Double edged sword b/c I can also see when I'm not making progress.
- Find this very time consuming, so I don't do this as much as I should.
- As I'm writing, I'm thinking about doing this more.
### Pair Programming
This means grab a friend and code together on one screen. I was super skeptical of this until I did this with a friend. I actually learned alot from the process! Probably the most undervalued process. If you've never done this, try it!
## Reactive Measures
### READ, GOOGLE, and THINK
If you can, read the error message, read the stack message, read your code!
Usually the error messages will be clear and will give you enough to at least Google.

Think about what's going on. You wrote the code, so you should be very familiar with it. Try to figure out what's going wrong.
Use dummy examples and walk through your code.
### The classic print statements
Sometimes you have no idea what's going on, so print stuff out.
Print out key variables that will give you an idea as to what's going on in your program. The point is for you to get on the same page as the program.
Try reproducing the error, then printing out in specific places where you suspect the error is happening. Following the error back to its source.
Sometimes more sophisticated examples are much easier to follow.
### Have a Friend Review Your Code
Sometimes your just in your own logic for too long. You might easily oversee things. Get a sanity check!
### Debuggers and Valgrind
Debuggers are programs that are created to help you debug. These tools are very powerful and as a result there is much to say. I will give a brief overview.
Debuggers allow you to set "breakpoints", which are essential points in your program that you can skip to. After you set a breakpoint, you can skip to that point and tell the debugger to take you step by step, line by line, through your program to figure out what's going on. Usually you'll be able to see the values of all relevant variables as you walk through your program.
Valgrind is a tool that you can use to help you find memory leaks and memory allocation.
Tutorials to
[gbd](https://www.cs.cmu.edu/~gilpin/tutorial/)
[lldb](https://lldb.llvm.org/tutorial.html)
[Stanford's Guide to valgrind](https://web.stanford.edu/class/cs107/guide/valgrind.html)
### For Memory Leaks
I wrote a small program to find memory leaks. Please lmk if there are bugs. Find it [here](https://github.com/gddh/JFJ).
-----------------------------------------
-----------------------------------------
-----------------------------------------
# Problem Sets
Exercise 1 - 4 from [day08 of piscine](https://github.com/gcamerli/bootcamp_c/blob/master/documents/days/d08.en.pdf)
# Solution Sets
1. .h
```
#ifndef FT_H
# define FT_H
int ft_putchar(char c);
void ft_putstr(char *str);
int ft_strcmp(char *s1, char *s2);
int ft_strlen(char *str);
void ft_swap(int *a, int *b);
#endif
```
2. .h
```
#ifndef FT_BOOLEAN_H
# define FT_BOOLEAN_H
# include <unistd.h>
# define TRUE 1
# define FALSE 0
# define SUCCESS 0
# define EVEN(x) x % 2 == 0
# define EVEN_MSG "I have an even number of arguments."
# define ODD_MSG "I have an odd number of arguments."
typedef int t_bool;
#endif
```
3. .h
```
#ifndef FT_ABS_H
# define FT_ABS_H
# define ABS(x) (x > 0 ? x : -x)
#endif
```
4. .h
```
#ifndef FT_POINT_H
# define FT_POINT_H
typedef struct s_point
{
int x;
int y;
} t_point;
void set_point(t_point *point);
#endif
```