Note 4 Dynamic Memory Allocation - [Homepage](http://gddh.github.io/)
========================
(Credits go to Berkeley CS61C's staff for teaching this material)
Let's start with looking at how Geeks for Geeks illustrates the memory layout.

We will first briefly the following parts of memory: text, and data parts.
Then focus on the parts that we need, the stack and the heap.
## Unitialized and Initialized Data
- Variables declared outside functions.
- The data does not grow or shrink.
- This means prior to runtime, we must know how much space this data will take. Loaded when program starts, can be modified.
- Examples of these variables are the global and static variables.
- If these variables are initialized to some value, it will be part of the initialized data.
- If not, it will be initialized to zero and allotted to the unitialized data.
## Text
This is the code. Has read-only access.
## Stack
- The stack is a Last In First Out (LIFO) data structure, meaning that the last thing we put into the data structure will be the first thing to come out when we remove from it.
- For more on the stack data structure, refer to the data structure notes. (Not necessary to understand dynamic memory allocation)
- On most systems, the stack grows downwards into free memory.
- The stack holds variables declared inside the functions and the "stack frames" that you see when you use Python tutor.
- Stack stores the frames', the arguments, space for the local variables, and some metadata to indicate where the frame presides in memory.
- "When a function is called, a block is reserved on the top of the stack for local variables and some bookkeeping data. When that function returns, the block becomes unused and can be used the next time a function is called." (https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap)
- The author of the post says "top" because it is a term to refer to the latest thing that was put onto the stack, but s/he really means bottom because our stack grows downward'
- The memory management in the stack is done automatically.
- We do **not** call malloc to make a function call or declare a local variable.
- Also when we finish a function call, we do **not** need to call free to free the memory.
- When we finish with a function call, the stack pointer will be automatically adjusted to where we were in the previous frame and the frame that the function call used will be freed.
- Share free memory with heap. If the stack meets the heap, it means that we've exhausted free memory.
## What is Dynamic Memory Allocation?
This section goes over the general idea of dynamic memory allocation.
So far, our memory needs have been limited within the scope of each function call.
To be clear, copy and paste the following code in the [python_tutor](http://pythontutor.com/c.html#mode=display).
```
char *some_function(void)
{
char str[] = "hello_world";
return (str);
}
int main(void)
{
char *g_str;
g_str = some_function();
printf("%s\n", g_str);
}
```
Note: ```str``` disappears! The space for the string was automatically allocated on the stack.
So how do we keep it? We can allocate space in the heap.
```
char *some_function(void)
{
char *str = (char *)malloc(sizeof(char) * 12);
str = "Hello_world";
return (str);
}
int main(void)
{
char *g_str;
g_str = some_function();
printf("%s\n", g_str);
}
```
**Conclusion**: Dynamic memory allocation allows us to manually manage parts of memory. This allows us to keep our memory across stack frames, but it also requires that we free our memory when we no longer need it.
**Note:** In the first program, if we had written ```char str* = "hello_world";``` instead of ```char str[] = "hello_world";```, it would work because string literals are in the read only section. For more, refer to note 02 strings for string literals. (not there now, but will be updated)
The following section goes over a specific example, ```make_word_array```, further explaining why we need dynamic memory allocation. It can be skipped, but it leads into the section ```ft_split_whitespaces```
## Why Dynamic Memory Allocation and Heap?
Consider a problem where we are given a string of words, and we would like to create an array with each word in its own box.
The function,```make_word_array```, creates an array and fills the array with words of a given string. The natural return value would be to return the array, but we cannot do this. We cannot do this because as soon as we complete the function call, the frame for the function call and all its local variables (including the array of strings) will be freed automatically because its on the stack.
**Further clarification**, suppose the ```main()``` (without loss of generality to any other functions), makes a call to ```make_word_array```. The stack will automatically allocate space for a new stack frame. ```make_word_array``` will declare an array so that it can store words into the array. That array will be stored in the stack frame because we declared the array in the function. After ```make_word_array``` fills the array, ```make_word_array``` will return the array. The stack pointer will leave the function and the computer will free the stack frame used by ```make_word_array```. This means that the array that ```make_word_array``` created was just freed because it was stored in the stack frame.
The example illustrates that the stack's memory does not persist across function calls. So how do we keep that array? Some might suggest using a static/global variable. The problem is that the memory space would persist throughout the execution of the entire program, but we would only use the memory space for a fraction of the time. We also would need to know how much memory to allocate before runtime. Thus the solution is the flexibility of dynamic memory allocation using the heap.
## Heap
Heap is the memory that is set aside for dynamic memory allocation. It is the place in memory where we allocate and free memory. Where we keep the things we ```malloc``` and ```free```
**Extra on Heaps**
This makes it much more complex to keep track of which parts of the heap are allocated or freed at any given time. (there are many custom heap allocators available to tune heap performance for different usage patterns. refer to above link for original source) Underlying operating system allows malloc library to ask for blocks of memory to use in heap. This is why your C code is dependent on particular operating systems. There are entire classes on how the memory is actually allocated by the OS and how it can be optimized. For now, let us focus on how we use the heap.
There are three basic functions that the Piscine needs.
1. ```malloc```
2. ```realloc``` (We will visit this when we discuss ```read``` and ```write```)
3. ```free```
### ```malloc()```
```malloc``` will ask for a block of memory from the computer. This block of memory will be placed into the heap.
**Malloc Declaration:** ```void *malloc(size_t size)```
- Malloc takes a size (in bytes) that you want to allocate and returns a void pointer. (size_t is an unsigned integer type big enough to “count” memory bytes).
- The void pointer points to first byte of the memory space that it allocated. (If the pointer is NULL, malloc ran out of space.)
- It is part of the standard library, so be sure to ```#include <stdlib.h>```
Note two things for malloc: ```(type *)malloc(sizeof(type) * number of types)```
1. The ```sizeof``` call can compute the size of the argument in bytes. This is a fast way to find the number of bytes for any type.
2. ```malloc``` is returning a void pointer. Recall the importance of declaring types from Note 01. For those reasons, when we malloc, we should always cast the data type.
- If we don't, we cannot:
- do pointer arithmetic
- dereference
- We can use void * for things like sorting/comparison. We'll cover this later on.
```
#include <stdlib.h>
int main()
{
char *str;
/* Initial memory allocation */
str = (char *) malloc(15);
}
```
### ```free()```
When we ```malloc```, we take space. If we keep taking space, we will run out of space. Therefore, we must remember to give that space back when we don't need it. We give it back using ```free```, the memory we no longer need.
The space we take is the space that the stack and the heap share. We take that space and put it into the heap.
Free Declaration: ```void free(void *ptr)```
- Free takes a void pointer to the memory block that was allocated by a call to ```malloc```, ```calloc```, or ```realloc```.
- Even though free takes a void pointer, **most** of the time, C will figure things out if you don't cast your pointers back to void.
- If the pointer is NULL, no action will be taken.
- It is part of the standard library, so be sure to ```#include <stdlib.h>```
```
int *ip;
ip = (int *ip) malloc(sizeof(int))
free((void *) ip);
```
A couple of points on ```free```
- you **must** pass the address that the allocation function (i.e. ```malloc```) gave you.
- You cannot do pointer arithmetic such as ```ip++``` followed by ```free(ip)``` because we changed the address that ip holds.
- If you forget to deallocate memory, you get a “Memory Leak”
- If you call free twice on the same memory it is called a “Double Free”
- This may crash your computer
- If you use data after freeing it, your program may crash.
- If you've created an array of strings or a 2D array, you loop through the array freeing each element, and then free the pointer to the array.
- I like to think about this as a pointer to pointers. We've got to free each of the pointers that the original pointer is pointing to, then the original pointer.
- An example
```
for (i = 0; i < m; i++) /* m is the the number of pointers */
{
free(a[i]);
}
free(a);
```
When deciding whether or not to ```free```, a good rule of thumb is to free anything that you created by ```malloc``` (or its variants).
### Example: ft_memdel
Run the following code [here](http://pythontutor.com/c.html#mode=display).
```
#include <string.h>
#include <stdio.h>
void ft_memdel(void **ap)
{
if (ap)
{
free(*ap);
*ap = NULL;
}
}
int main(void)
{
char *ptr = (char *)malloc(sizeof(char) * 12);
strcpy(ptr,"hello_world");
ft_memdel(&ptr);
}
```
Step 2: allocate space on the heap. ```ptr``` holds the memory address that begins this space.
Step 3: Fill the space with ```hello_world```.
Step 4: Call ```ft_memdel``` with the address of ```ptr```.
- In other words, we call ```ft_memdel``` with a pointer to a pointer.
Step 5: ap holds the address of ```ptr```.
- To be clear observe following *dereference operation*:
- ```ap``` holds the memory address of the pointer ```ptr.```
- ```*ap``` holds the memory address that begins the space that we allocated. (step 2)
- ```*ap``` is pointing to the same thing that `ptr` is pointing to.
- ```*ap``` has the same value that `ptr` has.
- ```**ap``` is the first character in the string.
Step 6: Check if ap is NULL. If it is, we can't dereference it in the next step.
- Feel free to check this out by:
- setting changing the first two lines in main to ```char **ptr = NULL;```
- Removing the if check
Step 7: Free the value that ```*ap``` points to. `Free` goes to the memory address that `*ap` holds and gives the memory back to the computer.
Step 8: Set the pointer to ```NULL``` so that we never accidentally use it again.
**Note:** In step 3. If we do ```ptr = "hello_world";```, `ptr` will not point to the memory space that we've allocated anymore. It will point to the string literal "hello_world".
## ft_split_whitespaces
Let's try tackling ```make_word_array```, which is actually ```ft_split_whitespaces```.
### Instructions
Create a function that splits a strings of characters into words. You are allowed to use malloc. Separators are spaces, tabs, and line breaks.
This function returns an array where each box contains a character-string's address represented by a word. The last element of this array should be equal to 0 to emphasize the end of the array.
There can't be any empty strings in your array. Draw th necessary conclusion.
The given string cannot be modified.
The prototype: ```char **ft_split_whitespaces(char *str)```
### Thought Process
1. The problem and the return type of the prototype tells us that we will need to ```malloc``` an array of strings, or a pointer to pointers to characters.
- Malloc needs a size, and since we are cretaing an array of strings we need to figure out how many strings there are. The number of strings is determined by the number of words, so we need to count the words.
- To count the words, we will need to traverse the string.
- We need to figure out whether or not a part of the string is a word. We can do this by identifying the beginning of a word, which is either:
- The beginning of a string (index 0) and the character is not a whitespace
- Somewhere in the string and the current character is not a whitespace, but the previous was.
2. We need to store each word in the string in the 2D array we creating. In the previous step, we created pointers that each point to a string. We will now be creating the string. (Why can't we simply point back to the string) We will need:
- Iterate through the string and when we come across the words store the word
- To store the word, we will need to malloc space for that word
- This means we'll need to count the number of characters to allocate space properly
- At the end of each string, we need to include a null terminator
- At the end of the array we need to include zero and return the array.
### Coding Walkthrough
#### Getting Word Count and Setting up
Let's tackle number 1 from the thought process.
```
#include <stdlib.h>
int is_whitespace(char c)
{
return ((c > 8 && c < 14) || (c == ' '));
}
int word_begins(char *str, int i)
{
if (i == 0)
return (!is_whitespace(str[i]));
else
return (!is_whitespace(str[i]) &&
is_whitespace(str[i - 1]));
}
int count_words(char *str)
{
int i;
int word_count;
i = 0;
word_count = 0;
while (str[i] != '\0')
{
if(word_begins(str, i))
word_count = word_count + 1;
i = i + 1;
}
return word_count;
}
char **ft_split_whitespaces(char *str)
{
char **word_p;
word_p = (char **)malloc(sizeof(char *) * (count_words(str) + 1));
}
```
The first thing that I have to do is allocate the array of strings (which are character pointers) by malloc. When I malloc, I have to cast to the proper type, which in this case is a pointer to pointers to characters. We will allocate the size of a character pointer times the number of words, so we will need to count the number of words. We add one for the null terminator.
We count the number of words by traversing through the string and checking if a word begins or not. Every time it begins we up the count.
A word can begin under two circumstances. If the first character in our string is not a whitespace, then by the problem's definition it is a word. If we are somewhere in the string, the current position is not a whitespace and the previous was a whitespace, then we also have a word.
#### Filling Array of Words - Allocating Space
```
int count_char(char *str, int i)
{
int char_count;
char_count = 0;
while (str[i] !='\0' && !is_whitespace(str[i]))
{
char_count = char_count + 1;
i = i + 1;
}
return char_count;
}
char **ft_split_whitespaces(char *str)
{
int i;
int w_i;
int p_i;
char **word_p;
i = 0;
p_i = 0;
word_p = (char **)malloc(sizeof(char *) * (count_words(str) + 1));
while (str[i] != '\0')
{
if (word_begins(str, i))
{
w_i = 0;
word_p[p_i] = (char *)malloc(sizeof(char) * (count_char(str, i) + 1));
/* this function is unfinished */
}
else
i = i + 1;
}
}
```
We then need to traverse through the string and fill up ```word_p```, our array of words. As we are traversing, we can split every index into two cases. Either a word begins or it doesn't. If it doesn't we just keep going. If a word begins, we will need to allocate space to store the word. To figure out the necessary space, we need to count the characters in that word.
To count the characters, we traverse the string until we either hit the end of the string or hit a whitespace. During the traversal, we will count the characters in the word.
#### Filling Array of Words - Filling Strings
```
char **ft_split_whitespaces(char *str)
{
int i;
int w_i;
int p_i;
char **word_p;
i = 0;
p_i = 0;
word_p = (char **)malloc(sizeof(char *) * (count_words(str) + 1));
while (str[i] != '\0')
{
if (word_begins(str, i))
{
w_i = 0;
word_p[p_i] = (char *)malloc(sizeof(char) * (count_char(str, i) + 1));
while (str[i] != '\0' && !is_whitespace(str[i]))
{
word_p[p_i][w_i] = str[i];
w_i = w_i + 1;
i = i + 1;
}
word_p[p_i][w_i] = '\0';
p_i = p_i + 1;
}
else
i = i + 1;
}
word_p[p_i] = 0;
return (word_p);
}
```
Once we've allocated the space, we do the same traversal as the character count. This time, instead of counting, we store the character into the array of words. We store it at ```p_i```, which will indicate which pointer/word and ```w_i```, the word_index, which will indicate where the character goes in the word. Of course, we end witha null terminator for the string.
At the end of our function, we put 0 at the end of the array.
Then we return the word pointer.
#### Testing. Always Test.
To test it, we write a simple main. Note how we can easily use a function that we previously created.
```
int main(int argc, char **argv)
{
char **p;
int i;
if (argc == 2)
{
p = ft_split_whitespaces(argv[1]);
i = 0;
while (i < count_words(argv[1]))
{
printf("%s\n", p[i]);
i = i + 1;
}
}
}
```
This is by no means the most efficient way to code it up, but I think it is one of the easiest ways to translate English to code. In other words, I think this is one of the most readable ways to tackle the problem.
Please lmk if you find bugs!
## Personal Experience and Tips
This section is just me sharing some experiences and peronal tips.
At this point, I hope these notes have been helpful for you, and if you've followed from the beginning, you've done well.
CS is a tough to learn, at least for me it was. It's painful when you've been working hours and you still don't know how to tackle an "easy problem", when you don't understand the code, when you can't find a bug even though you've tried to look for it over and over again... When I first started, I felt frustrated, behind, and slow. If you do to, don't let it get to you. I found most if not all students feel that way at some point. The successful ones were the ones that kept trying. Despite failing, they rewatched lectures, attended multiple discussion sections, drilled questions, worked together, asked questions, answered questions, analyzed their mistakes, kept trying to improve, and always tried to understand. At some point they found what works for them! Keep it up the hard work!
### Some Tips
- Find people around your level of understanding and work with them. Ask each other questions! Work on the material together. Even if you have a solution, learning how another person did can be just as valuable if not more valuable.
- This is actually very important. I learned so much from working with other people.
- I personally look for the following
- If their work ethic is a close match to yours.
- If they have a different background (not CS background, just life experience) from you.
- Do you like them as people
- Work together as a team! Succeed together!
- Teach! The best way to test your mastery over the material is to teach someone. Ask them to question you. Don't forget to be patient and understanding.
- Try to streamline your studying
- When I first started CS, I rewrote lecture notes we were given because I thought that was the best way for me to internalize the material as it forced me to think about the material. The problem was it was very time consuming. A friend told me to just read it, think about it, and repeat if you don't understand. Long story short, I found that just reading and thinking really worked.
- "thinking" besides, literally thinking, I occasionally wrote down phrases, always worked through examples, looked up/asked about things I didn't understand, discussed with my team, and kept challenging my own understanding of the material.
- Find people you can consistently rely on to teach you harder concepts, work through harder problems, or check your work
- Make sure that you've exhausted all other options and really made an effort before approaching these people for help. For two reasons
- you will learn more by doing so
- If you don't, you aren't respecting the other person.
- Do not rely on another person's understanding. If your team member/partner understands the concepts, that is not enough. Make sure you understand it as well.
- This goes both ways. Make sure your partner and team member understands the concepts.
- Look for better resources if the one you are using is not good enough.
- Maybe the book is too tense or too sparse. Try reading another one
- Maybe the videos you're watching are too slow. Try something else.
- Be consistent with your hours. A routine helps.
# Problem Set
1. Write a program that displays the number of arguments passed to it, followed by a newline. If there are no arguments, just display a 0 followed by a newline.
- Example
```
$>./paramsum 1 2 3 5 7 24
6
$>./paramsum 6 12 24 | cat -e
3$
$>./paramsum | cat -e
0$
```
2. Write the following function:```int *ft_range(int start, int end); ``` It must allocate (with malloc()) an array of integers, fill it with consecutiver values that begin at start and end at end (Including start and end !), then return a pointer to the first value of the array.
- Examples:
- With (1, 3) you will return an array containing 1, 2 and 3.
- With (-1, 2) you will return an array containing -1, 0, 1 and 2.
- With (0, 0) you will return an array containing 0.
- With (0, -3) you will return an array containing 0, -1, -2 and -3.
3. Write the following function:``` int *ft_rrange(int start, int end); ```It must allocate (with malloc()) an array of integers, fill it with consecutive values that begin at end and end at start (Including start and end !), then return a pointer to the first value of the array.
- Examples:
- With (1, 3) you will return an array containing 3, 2 and 1
- With (-1, 2) you will return an array containing 2, 1, 0 and -1.
- With (0, 0) you will return an array containing 0.
- With (0, -3) you will return an array containing -3, -2, -1 and 0.
4. Reproduce the behavior of strdup. The prototype is ```char *ft_strdup(char *src)```
5. Create a function that splits a strings of characters into words. You are allowed to use malloc. Separators are spaces, tabs, and line breaks.
This function returns an array where each box contains a character-string's address represented by a word. The last element of this array should be equal to 0 to emphasize the end of the array.
There can't be any empty strings in your array. Draw th necessary conclusion. The given string cannot be modified.
The prototype: ```char **ft_split_whitespaces(char *str)```
6. Create a function that transforms arguments given as command-line into a single string of characters. To be clear don't include the executable (this may be different from the piscine exercise). The prototype is:
```char *ft_concat_params(int argc, char **argv)```
# Problem Set Review
1. Write ft_strcmp
2. Write ft_atoi
3. Write a program that takes a string and displays its last word followed by a \\n. A word is a section of string delimited by spaces/tabs or by the start/end of the string. If the number of parameters is not 1, or there are no words, display a newline. You can use the write function.
- Examples
```
$> ./last_word "FOR PONY" | cat -e
PONY$
$> ./last_word "this ... is sparta, then again, maybe not" | cat -e
not$
$> ./last_word " " | cat -e
$
$> ./last_word "a" "b" | cat -e
$
$> ./last_word " lorem,ipsum " | cat -e
lorem,ipsum$
```
4. Write a program that takes two strings and displays, without doubles, the characters that appear in either one of the strings. The display will be in the order characters appear in the command line, and will be followed by a \n. If the number of arguments is not 2, the program displays \n.
- Examples
```
$>./union zpadinton "paqefwtdjetyiytjneytjoeyjnejeyj" | cat -e
zpadintoqefwjy$
$>./union ddf6vewg64f gtwthgdwthdwfteewhrtag6h4ffdhsd | cat -e
df6vewg4thras$
$>./union "rien" "cette phrase ne cache rien" | cat -e
rienct phas$
$>./union | cat -e
$
$>./union "rien" | cat -e
$
```
5. Write a program that takes two strings and displays, without doubles, the characters that appear in both strings, in the order they appear in the first one. The display will be followed by a \n. If the number of arguments is not 2, the program displays \n.
- Examples
```
$>./inter "padinton" "paqefwtdjetyiytjneytjoeyjnejeyj" | cat -e
padinto$
$>./inter ddf6vewg64f gtwthgdwthdwfteewhrtag6h4ffdhsd | cat -e
df6ewg4$
$>./inter "rien" "cette phrase ne cache rien" | cat -e
rien$
$>./inter | cat -e
$
```
6. Write a program that takes a string and displays it, replacing each of its letters by the letter 13 spaces ahead in alphabetical order. 'z' becomes 'm' and 'Z' becomes 'M'. Case remains unaffected. The output will be followed by a newline. If the number of arguments is not 1, the program displays a newline.
- Examples:
```
$>./rot_13 "abc"
nop
$>./rot_13 "My horse is Amazing." | cat -e
Zl ubefr vf Nznmvat.$
$>./rot_13 "AkjhZ zLKIJz , 23y " | cat -e
NxwuM mYXVWm , 23l $
$>./rot_13 | cat -e
$
$>./rot_13 "" | cat -e
$
```
# Problem Set Solution
1. paramsum
```
#include <unistd.h>
void ft_putchar(char c)
{
write(1, &c, 1);
}
void ft_putnbr(int x)
{
if (x <= 0 && x >= 9)
ft_putchar(x + '0');
else
{
ft_putnbr(x / 10);
ft_putnbr(x % 10);
}
}
int main(int argc, char **argv)
{
argv[1] = "0";
if (x >= 0)
ft_putnbr(argc - 1);
ft_putchar('\n');
return (0);
}
```
2. ft_range
```
#include <stdlib.h>
#include <stdio.h>
int get_diff(start, end)
{
int diff;
diff = start - end;
if (diff < 0)
diff = diff * -1;
return (diff + 1);
}
void count_up(int start, int end, int *p)
{
int i;
i = 0;
while (start <= end)
{
p[i] = start;
start = start + 1;
i = i + 1;
}
}
void count_down(int start, int end, int *p)
{
int i;
i = 0;
while (start >= end)
{
p[i] = start;
start = start - 1;
i = i + 1;
}
}
int *ft_range(int start, int end)
{
int *p;
p = (int *)malloc(sizeof(int) * get_diff(start, end));
if (start < end)
count_up(start, end, p);
else if (start > end)
count_down(start, end, p);
else
p[0] = start;
return p;
}
int main(int argc, char **argv)
{
int *p;
int i;
p = ft_range(atoi(argv[1]), atoi(argv[2]));
i = 0;
while(i < get_diff(atoi(argv[1]), atoi(argv[2])))
{
printf("%d, ", p[i]);
i = i + 1;
}
printf("\n");
}
```
3. ft_rrange
```
#include <stdlib.h>
#include <stdio.h>
int get_diff(start, end)
{
int diff;
diff = start - end;
if (diff < 0)
diff = diff * -1;
return (diff + 1);
}
void count_up(int start, int end, int *p)
{
int i;
i = 0;
while (end <= start)
{
p[i] = end;
end = end + 1;
i = i + 1;
}
}
void count_down(int start, int end, int *p)
{
int i;
i = 0;
while (end >= start)
{
p[i] = end;
end = end - 1;
i = i + 1;
}
}
int *ft_rrange(int start, int end)
{
int *p;
p = (int *)malloc(sizeof(int) * get_diff(start, end));
if (start < end)
count_down(start, end, p);
else if (start > end)
count_up(start, end, p);
else
p[0] = start;
return p;
}
int main(int argc, char **argv)
{
int *p;
int i;
p = ft_rrange(atoi(argv[1]), atoi(argv[2]));
i = 0;
while(i < get_diff(atoi(argv[1]), atoi(argv[2])))
{
printf("%d, ", p[i]);
i = i + 1;
}
printf("\n");
}
```
4. ft_strdup
```
#include <stdlib.h>
int ft_strlen(char *str)
{
int i;
i = 0;
while(str[i] != '\0')
i = i + 1;
return (i);
}
char *ft_strcpy(char *dest, char *src)
{
int i;
i = 0;
while (src[i] != '\0')
{
dest[i] = src[i];
i = i + 1;
}
dest[i] = '\0';
return (dest);
}
char *ft_strdup(char *src)
{
char *dest;
dest = (char *)malloc(sizeof(char) * (ft_strlen(src) + 1));
if (dest == NULL)
return (NULL);
ft_strcpy(dest, src);
return (dest);
}
int main(int argc, char **argv)
{
char *dup;
if (argc == 2)
{
dup = ft_strdup(argv[1]);
printf("%s\n", dup);
}
}
```
5. ft_split_whitespace
```
#include <stdlib.h>
#include <stdio.h>
int is_whitespace(char c)
{
return ((c > 8 && c < 14) || (c == ' '));
}
int word_begins(char *str, int i)
{
if (i == 0)
return (!is_whitespace(str[i]));
else
return (!is_whitespace(str[i]) &&
is_whitespace(str[i - 1]));
}
int count_words(char *str)
{
int i;
int word_count;
i = 0;
word_count = 0;
while (str[i] != '\0')
{
if(word_begins(str, i))
word_count = word_count + 1;
i = i + 1;
}
return word_count;
}
int count_char(char *str, int i)
{
int char_count;
char_count = 0;
while (str[i] !='\0' && !is_whitespace(str[i]))
{
char_count = char_count + 1;
i = i + 1;
}
return char_count;
}
char **ft_split_whitespaces(char *str)
{
int i;
int w_i;
int p_i;
char **word_p;
i = 0;
p_i = 0;
word_p = (char **)malloc(sizeof(char *) * (count_words(str) + 1));
while (str[i] != '\0')
{
if (word_begins(str, i))
{
w_i = 0;
word_p[p_i] = (char *)malloc(sizeof(char) * (count_char(str, i) + 1));
while (str[i] != '\0' && !is_whitespace(str[i]))
{
word_p[p_i][w_i] = str[i];
w_i = w_i + 1;
i = i + 1;
}
word_p[p_i][w_i] = '\0';
p_i = p_i + 1;
}
else
i = i + 1;
}
word_p[p_i] = 0;
return (word_p);
}
int main(int argc, char **argv)
{
char **p;
int i;
if (argc == 2)
{
p = ft_split_whitespaces(argv[1]);
i = 0;
while (i < count_words(argv[1]))
{
printf("%s\n", p[i]);
i = i + 1;
}
}
}
```
6.ft_concat_params.
```
#include <stdio.h>
#include <stdlib.h>
int get_size(int argc, char **argv)
{
int i;
int j;
int count;
i = 1;
count = 0;
while (i < argc)
{
j = 0;
while (argv[i][j] != '\0')
{
count = count + 1;
j = j + 1;
}
count = count + 1;
i = i + 1;
}
return (count + 1);
}
void add_word(char *word, int *i, char *str)
{
int j;
j = 0;
while (word[j] != '\0')
{
str[*i] = word[j];
*i = *i + 1;
j = j + 1;
}
str[*i] = '\n';
*i = *i + 1;
}
char *ft_concat_params(int argc, char **argv)
{
char *str;
int w_i;
int i;
str = (char *)malloc(sizeof(char) * get_size(argc, argv));
if (str == NULL)
return NULL;
w_i = 1;
i = 0;
while (w_i < argc)
{
add_word(argv[w_i], &i, str);
w_i = w_i + 1;
}
str[i] = '\0';
return str;
}
int main(int argc, char **argv)
{
char *str;
if (argc > 1)
{
str = ft_concat_params(argc, argv);
printf("%s", str);
}
}
```
# Review Problems Solution
1. ft_strcmp
```
int ft_strcmp(char *str1, char *str2)
{
int i;
i = 0;
while (str1 != '\0' && str1[i] == str2[i])
i = i + 1;
return (unsigned char)str1[i] - (unsigned char)str2[i];
}
```
Note the cast to unsigned char is unnecessary.
2. ft_atoi
```
int is_whitespace(char c)
{
return ((c > 8 && c < 14) || (c == ' '));
}
int is_nbr(char c)
{
return (c >= '0' && c <= '9');
}
int convert_num(char c)
{
return (c - '0');
}
int ft_atoi(char *str)
{
int i;
int neg;
int total;
i = 0;
neg = 1;
total = 0;
while (str[i] != '\0' && is_whitespace(str[i]))
i = i + 1;
if (str[i] == '+' || str[i] == '-')
{
if (str[i] == '-')
neg = -1;
i = i + 1;
}
while (str[i] != '\0' && is_nbr(str[i]))
{
total = total * 10 + convert_num(str[i]);
i = i + 1;
}
return (total * neg);
}
```
3. last_word
```
#include <unistd.h>
void ft_putchar(char c)
{
write(1, &c, 1);
}
int is_whitespace(char c)
{
return ((c > 8 && c < 14) || c == ' ');
}
int word_begins(char *str, int i)
{
if (i == 0)
return (!is_whitespace(str[i]));
else
return (!is_whitespace(str[i]) &&
is_whitespace(str[i - 1]));
}
void print_word_at(int i, char *str)
{
while (str[i] != '\0' && !is_whitespace(str[i]))
{
ft_putchar(str[i]);
i = i + 1;
}
}
void last_word(char *str)
{
int i;
int j;
int word_start;
i = 0;
j = -1;
while (str[i] != '\0')
{
if (word_begins(str, i))
j = i;
i = i + 1;
}
if (j != -1)
print_word_at(j, str);
}
int main(int argc, char **argv)
{
if (argc == 2)
{
last_word(argv[1]);
}
ft_putchar('\n');
}
```
4. union.c
```
#include <unistd.h>
void ft_putchar(char c)
{
write(1, &c, 1);
}
int is_repeat(char *str1, int end, char c)
{
int i;
i = 0;
while(i < end)
{
if (str1[i] == c)
return (1);
i = i + 1;
}
return (0);
}
void ft_union(char *str1, char *str2)
{
int i;
int j;
i = 0;
while (str1[i] != '\0')
{
if (!is_repeat(str1, i, str1[i]))
ft_putchar(str1[i]);
i = i + 1;
}
j = 0;
while(str2[j] != '\0')
{
if (!is_repeat(str1, i, str2[j]) &&
!is_repeat(str2, j, str2[j]))
ft_putchar(str2[j]);
j = j + 1;
}
}
int main(int argc, char **argv)
{
if (argc == 3)
{
ft_union(argv[1], argv[2]);
}
ft_putchar('\n');
}
```
5. inter.c
```
#include <unistd.h>
void ft_putchar(char c)
{
write(1, &c, 1);
}
int is_in(char *str, char c)
{
int i;
i = 0;
while (str[i] != '\0')
{
if (str[i] == c)
return (1);
i = i + 1;
}
return (0);
}
int is_repeat(char *str, int end, char c)
{
int i;
i = 0;
while (i < end)
{
if (str[i] == c)
return (1);
i = i + 1;
}
return (0);
}
void inter(char *str1, char *str2)
{
int i;
i = 0;
while (str1[i] != '\0')
{
if (!is_repeat(str1, i, str1[i]) &&
is_in(str2, str1[i]))
ft_putchar(str1[i]);
i = i + 1;
}
}
int main(int argc, char **argv)
{
if (argc == 3)
{
inter(argv[1], argv[2]);
}
ft_putchar('\n');
}
```
6. rot_13
```
# include <unistd.h>
void ft_putchar(char c)
{
write(1, &c, 1);
}
char calc_rotation(char c, int rotate_by, char start)
{
c = c - start;
c = c + rotate_by;
c = c % 26;
c = c + start;
return (c);
}
void rotate_char(char c, int rotate_by)
{
if (c >= 'A' && c <= 'Z')
c = calc_rotation(c, rotate_by, 'A');
else if (c >= 'a' && c <= 'z')
c = calc_rotation(c, rotate_by, 'a');
ft_putchar(c);
}
void rotate_str(char *str, int rotate_by)
{
int i;
i = 0;
while(str[i] != '\0')
{
rotate_char(str[i], rotate_by);
i = i + 1;
}
}
int main(int argc, char **argv)
{
if (argc == 2)
{
rotate_str(argv[1], 13);
}
ft_putchar('\n');
}
```