1337 | libft | Static library in C

AHMED EZ-ZOUINE
17 min readNov 19, 2023

--

This note was written to help my classmates understand the subject “libft” (school 1337).

The libft is the first project of the “common core” of school 1337. There are different concepts already seen during the entrance contest (also called “the pool”).

Get ready for some coding fun with Libft! It’s the first project you’ll tackle at 1337, and it’s all about creating a supercharged library of awesome functions in good ol’ C. We’re talking about functions that give the standard ones a run for their money!

To make things even more exciting, you’ll be using a Makefile to compile and run your library. Say goodbye to those ordinary standard functions because you won’t be needing them here. Libft is your new go-to resource for all your future 1337 adventures.

ince in most 1337 projects we can’t use any of the standard C library functions, the goal of this first project is to replicate some of the usual functions ourselves, as well as some additional functions.
We have to build a library that we will be able to use in our future projects.

Part 1 :

Alright, let’s kick off this project with a blast! In this first phase, your mission is to revamp a bunch of libc functions, just like the ones you find in their manual pages. But here’s the twist: you need to give them a funky makeover by adding “ft_” as a prefix to their names. So, for example, strlen becomes ft_strlen. Cool, right?

But wait, there’s a catch ! Your revamped functions should have the exact same prototypes and behaviors as the original ones. We’re talking about clones here! So, when someone uses your ft_strlen, they should get the same results and behavior as if they had used the original strlen.

It’s a challenge, but it’s also an opportunity to show off your coding skills and attention to detail. So, roll up your sleeves, put on your coding cap, and let’s give these libc functions a hip new vibe with the power of “ft_”!

ft_isalpha

Subject :

ISALPHA(3) (simplified)

NAME
isalpha -- alphabetic character test
SYNOPSIS
int isalpha(int c)
DECRIPTION
The isalpha() function tests for any character for which isupper(3) or
islower(3) is true. The value of the argument must be resprensentable as
an unsigned char or the value of EOF.

RETURN VALUES
The isalpha() function return zero if the character tests false and
returns non-zero if the character tests true.

Understandable explanation

For this function, the man is self-explanatory, but I’ll still explain it in other words. The isalpha() function returns a non-zero value if the character passed as an int parameter is an alphabetical letter (lowercase or uppercase).If the character is not alphabetical, the isalpha() function returns 0.

#include "libft.h"

int ft_isalpha(int c)
{

if ((c >= 65 && c <= 90) || (c >= 97 && <= 122))
return (c);
return (0);
}

ft_isdigit

Understandable explanation :

For this function, the man is self-explanatory, but I’ll still explain it in other words. The isdigit() function return a non-zero value if the character passed as an int parameter is a decimal digit character (0 - 9). If the character is not a decimal digit character, the isdigit() function return 0.

#include "libft.h"

int ft_isdigit(int c)
{
if (c >= 48 && c <= 57)
return (c);
return (0);
}

ft_islnum

Understandable explanation :

For this function, the man is self-explanatory, but I’ll still explain it in other words. The isalnum() function returns a non-zero value if the character passed as an int parameter is alphabetical or a decimal digit character. If the character is not alphabetical nor a decimal digit character, the isalnum() function returns 0.

#include "libft.h"

int ft_isalnum(int c)
{
/* This checks makes use of the 2 preceeding functions we built */
if (ft_isalpha(c) || ft_isdigit(c))
return (c); //If we reach this point we can return c as it will be a non-zero value
return (0);
}

ft_isascii

Understandable explanation :

For this function, the man is self-explanatory, even though it doesn’t tell you what the return values are…

The isascii() function returns a non-zero value if the character passed as an int parameter is an ASCII character between 0 and octal 0177, this means characters between 0 and decimal 127, all characters displayed when you type the man ascii command. If the character is not an ASCII character between 0 and octal 0177, the isascii() function return 0.

#include "libft.h"

int ft_isascii(int c)
{
if (c >= 0 && c <= 127)
return (1);
return (0);
}

ft_isprint

Understandable explanation

For this function, the man is pretty self-explanatory, but I’ll give more details (i.e. what are the printing characters). The isprint() function returns a non-zero value if the character passed as an int parameter is a printing character. If the character is not a printing character, the isprint() function returns 0. The printing characters are all character between decimal 32 and decimal 126.

#include "libft.h"

int ft_isprint(int c)
{
if (c >= 32 && c <= 126)
return (c);
return (0);
}

ft_bzero

Understandable explanation

This function works the same way as the memset() function, except you don't have to specify what character to write, it'll always be 0 (NUL character). This function does not return anything and if the number of characters to write you passed as size_t n is 0, bzero does nothing.

#include "libft.h"

void ft_bzero(void *s, size_t n)
{
while (n--)
*(unsigned char *)s++ = 0;
}

ft_memset

Understandable explanation

As the man description says, this function writes len bytes of value c to the string b. The value of c will be converted to an unsigned char, so to set this value in the b string, we'll have to convert the b string to a pointer to unsigned char. But remember the return value, we have to return the first parameter of the function, the void *b string. So how do we convert this parameter without changing the original one ? Think about temporary variables.

#include "libft.h"

void *ft_memset(void *b, int c, size_t len)
{
unsigned char *tmp_ptr;

tmp_ptr = (unsigned char *) b;
while (len > 0)
{
*(tmp_ptr++) = (unsigned char) c;
len--;
}
return (b);
}

ft_memcpy

Understandable explanation

The memcpy function copies maximum n bytes from src to dst. The man talks about memory overlapping, I'll explain this with details on the memmove function page. As for memset and bzero we'll need some temporary pointers to manipulate our data. This functions works like the strcpy function, except that memcpy accepts void * as parameters, so we can give it any type of pointer we want to copy.

#include "libft.h"

void *ft_memcpy(void *dst, const void *src, size_t n)
{
unsigned char *tmp_dst;
unsigned char *tmp_src;
// funtion return pointer void So convert 0 to (void *)
if (dst == (void *)0 && src == (void *)0)
return (dst);

tmp_dst = (unsigned char *) dst;
tmp_src = (unsigned char *) src;

while (n > 0)
{

*(tmp_dst++) = *(tmp_src++);
n--;
}
return (dst);
}

ft_memmove

Understandable explanation

The memmove() function does the same thing as the memcpy() function but this time, the copy is made, as said in the man, in a non-destructive manner. This means that both strings (src and dst) can overlap in memory and this function does not overwrite part of, or the entirety of the string when making the copy.

I found a really good explanation so I’ll copy it here to explain what is memory overlapping.

#include "libft.h"

void *ft_memmove(void *dst, const void *src, size_t len)
{
char *c_src;
char *c_dst;
size_t i;

if (!dst && !src)
return (NULL);

c_src = (char *) src;
c_dst = (char *) dst;
i = 0;

if (c_dst > c_src)
while (len-- > 0)
c_dst[len] = c_src[len];

else
{
while (i++ < len)
c_dst[i] = c_src[i];
}
return (dst);

ft_memcmp

Understandable explanation

memcmp() compares byte strings. It works similarly to the strncmp() function.

The difference here is that memcmp() works with bytes strings so it take void pointers as parameter, plus a third character, representing, as said in the man, the assumed length of both strings. This means that memcmp() will not compare more than n bytes.The return value depends on what difference is found. If there is no difference between both strings, the return value will be 0.If there is a difference, and the first different character in s2 is greater than the character at the same place in s1, the returned result will be negative. If there is a difference, and the first different character in s2 is less than the character at the same place in s1, the returned result will be positive.

#include "libft.h"

int ft_memcmp(const void *s1, const void *s2, size_t n)
{
unsigned char *str1;
unsigned char *str2;
size_t i;

str1 = (unsigned char) *s1;
str2 = (unsigned char) *s2;
i = 0;
while (i < n)
{
if ((unsigned char) str1[i] != (unsigned char) str2[i])
return ((unsigned char) str1[i] - (unsigned char) str2[i]);
}
return (0);
}

ft_memchr

understandable explanation

The memchr() function works similarly as the strchr() function, the difference is that memchr() works with byte string (void *) where strchr() works with 'litteral' strings (char *). This means we can send whatever type of data we want to memchr() and it'll still work. memchr() also has a third parameter, n. This parameter tells the function how many bytes we want to search in. We need this parameter since s is not a 'litteral' string, it doesn't have a NUL-terminating character. If we didn't have this parameter, we would be reading a random number of bytes each time.

#include "libft.h"

void *ft_memchr(const void *s, int c, size_t n)
{
unsigned char *str;
size_t i;
unsigned char uc;

str = (unsigned char *) s;
uc (unsigned char) c;
i = 0;
while (i < n)
{
if (str[i] == uc)
return ((void *) &str[i]);
i++;
}
return (NULL);
}

ft_strlcpy

Understandable explanation

What this function does is pretty simple in that it’s made to copy one string to another but with a small catch, it alwaysNUL-terminate the string.If you give a dstsize long enough to NUL-terminate the string without truncating it, strlcpy() will simply copy the string, as you'd do with strcpy(). If you don't give a dstsize long enough, it will copy dstsize - 1 characters from the source into the destination, adding the NUL-terminating character after that. The strlcpy() function always returns the length of the string that it tried to create, this is the length of src, even if you have to truncate the string to NUL-terminate it.

#include "libft.h"

size_t ft_strlcpy(char *dst, const char *src, size_t dstsize)
{
size_t src_len;
src_len = ft_strlen(src);

if (src_len + 1 < dstsize)
ft_memcpy(dst, src, src_len + 1);
else if (dstsize != 0)
{

ft_memcpy(dst, src, dstsize - 1);
dst[dstsize - 1] = 0;
}
return (src_len);
}

ft_strlcat

Understandable explanation

What this function does is pretty simple in that it’s made to concatenate two strings but with a small catch, it alwaysNUL-terminate the string. If you give a dstsize long enough to NUL-terminate the resulting concatenated string without truncating it, strlcat()will simply concatenate the two string, as you'd do with strcat(). If you don't give a dstsize long enough, it will concatenate dstsize - strlen(dst) - 1 characters, adding the NUL-terminating character after that. The strlcat() function always returns the length of the string it tried to create, this is the original length of dst plus the original length of src, even if you have to truncate the string to NUL-terminate it.

#include "libft.h"

size_t ft_strlcat(char *dst, const char *src, size_t dstsize)
{
size_t src_len;
size_t dst_len;

src_len = ft_strlen(src);
dst_len = ft_strlen(dst);
if (dst_len >= dstsize)
dst_len = dstsize;
if (dst_len == dstsize)
return (dstsize + src_len);
if (src_len < dstsize - dst_len)

ft_memcpy(dst + dst_len, src, src_len + 1);
else
{

ft_memcpy(dst + dst_len, src, dstsize - dst_len - 1);
dst[dstsize - 1] = '\0';
}
return (dst_len + src_len);
}

ft_strrchr

Understandable explanation

This function is fairly easy to understand, it does the same thing as strchr(), but locates the last occurence of c.

#include "libft.h"

char *ft_strrchr(const char *s, int c)
{
unsigned int i;
char *res;
char cc;

cc = (char) c;
res = NULL;
i = 0;
while (s[i])
{
if (s[i] == cc)
res = (char *) &s[i];
i++;
}
if (s[i] == c)
res = (char *) &s[i];
return (res);
}

ft_strchr

Understandable explanation

The strchr() function searches for one character in a string. If it finds the character, it returns a pointer to the first occurence of this specific character. If it don’t find any occurence of this character, it returns NULL. We also have to return a pointer to the character if the character is \0.

#include "libft.h"

char *ft_strchr(const char *s, int c)
{
unsigned int i;
char cc,

cc = (char) c;
i = 0;
while (s[i])
{
if (s[i] == cc)
return ((char *) &s[i]);

i++;
}

if (s[i] == cc)
return ((char *) &s[i]);
return (NULL);
}

ft_strnstr

Understandable explanation

The strnstr() function works in the same way as strchr() but searches for a complete substring in max n characters instead of a single character.

#include "libft.h"

char *ft_strnstr(const char *haystack, const char *needle, size_t len)
{
size_t i;
size_t j;

i = 0;
j = 0;
if (needle[0] == 0)
return ((char *) haystack);
while (haystack[i] && i < len)
{
while (haystack[i + j] == needle[j] && haystack[i + j] && i + j < len)
{
j++;
if (needle[j] == 0)
return ((char *) haystack + i);
}
i++;
j = 0;
}
return (0);
}

ft_strdup

Understandable explanation

For once, the man is really clear on what the function does. So I don’t think I need to explain it with more details.

Hints : We have to use malloc for this since the returned value of this function must be ‘freeable’ with the free function.

#include "libft.h"

char *ft_strdup(const char *s1)
{
char *dest;
size_t i;

dest = (char *) malloc(ft_strlen(s1) + 1);
if (!dest)
return (NULL);
i = 0;
while (s1[i])
{

dest[i] = s1[i];
i++;
}
dest[i] = 0;
return (dest);
}

ft_calloc

Understandable explanation

By now you should have understand what the malloc() function does, at least I hope. Otherwise, understand how malloc() works and come back here. I will mainly base my explanation on comparing calloc() to malloc().

calloc() works in the same way as malloc() does, but the difference is that calloc() sets all the memory bytes are set to 0 instead of staying as the gibberish that was there in memory before we allocated it.

#include "libft.h"

void *ft_calloc(size_t count, size_t size)
{
unsigned char *tmp;
size_t i;

i = 0;
tmp = malloc(count * size);
if (!tmp)
return (NULL);
while (i < count * size)
tmp[i++] = 0;
return (tmp);
}

Part 2

Now, it’s time to take this project to the next level in the second phase! You’ll be flexing your coding muscles by implementing a range of functions that either don’t exist in the libc library or have a unique twist to them.

These additional functions serve multiple purposes and are like precious building blocks for creating even more awesome functions. They open up a world of possibilities and give your project a boost in utility and versatility.

But what do these functions do, you ask? Well, they can do all sorts of magical things! They might handle complex calculations, manipulate strings in creative ways, or even perform specialized tasks that are crucial for the overall development of your project. The sky’s the limit!

So, get ready to dive into the exciting world of these unique functions. Let your creativity run wild and show off your coding wizardry by bringing something truly extraordinary to the table. The more you explore and incorporate these functions, the more incredible your project will become!

ft_substr

Understandable explanation

ft_substr returns a substring of the string s passed as parameter.

Here’s an example

ft_substr("Bonjour comment ca va?", 5, 8);
=> "ur comme"
#include "libft.h"

char *ft_substr(const char *s, unsigned int start, size_t len)
{
size_t i;
char *str;

if (!s)
return (NULL);
if (start > ft_strlen(s))
return (ft_strdup(""));
if (len > ft_strlen(s + start))
len = ft_strlen(s + start);
str = ft_calloc(len + 1, sizeof(char));
if (!str)
return (NULL);
i = 0;
while (i < len)
{
str[i] = s[start + i];
i++;
}
return (str);
}

ft_strjoin

Understandable explanation

This function works basically the same way as ft_strlcat does, but instead of passing it a destination string that has to be correctly allocated as a parameter, we only pass two strings and ft_strjoin will allocate the required memory for both of them plus the NUL-terminating character.

s1 will be the first string in the result, s2 the second one.

#include "libft.h"

char *ft_strjoin(const char *s1, const char *s2)
{
char *res;
int i;
int j;

i = 0;
j = 0;
res = (char *) malloc((ft_strlen(s1) + ft_strlen(s2) + 1) * sizeof(char));
if (!res)
return (NULL);
while (s1[i])
res[j++] = s1[i++];
i = 0;
while (s2[i])
res[j++] = s2[i];
res[j] = 0;
return (res);
}

ft_strtrim

Understandable explanation

The ft_strtrim() function takes a string and trims it.

What does trimming mean you might ask ? Let me explain. Trimming means removing the characters specified in the set string from the start AND the end of the string s1, without removing the characters from the set that are in the middle of s1. If we have the string ababaaaMy name is Simonbbaaabbad and our set is ab, we'll get this result out of the ft_strtrim() function : My name is Simon. We removed every a and b from the start and the end of s1, without touching at the a in the middle of s1.

#include "libft.h"

char *ft_strtrim(const char *s1, const char *set)
{
int i;
int j;

i = 0;
j = ft_strlen(s1) - 1;
if (ft_strlen(s1) == 0)
return (ft_strdup(""));

while (to_trim(set, s1[i]))
i++;
while (to_trim(set, s1[j]))
j--;
return (new_str(s1, i, j - (i - 1));
}


static char *new_str(const char *s1, size_t start, size_t len)
{
char *str;
size_t i;
if (len <= 0 || start >= ft_strlen(s1))
return (ft_strdup(""));
str = ft_calloc(len + 1, sizeof(char));
if (!str)
return (NULL);
i = 0;
while (i < len)
{
str[i] = s1[start + i];
i++;
}
return (str);
}


static int to_trim(const char *set, char c)
{
int i;

i = 0;
while (set[i])
{
if (c == set[i])
return (1);
i++;
}
return (0);
}

ft_split

Understandable explanation

The subject tells us that ft_split() must return an array of strings (=> an array of arrays, since strings are arrays of characters terminated by a NUL character).

We can also phrase that as an array of words, we take the string s and we split it to get an array containing each words of it. Each word is separated by one or more c, that's our word delimiter.

It’s also said that our words array must be NUL-terminated. That means we have to allocate one more element in our array, that we can set to 0. By doing this we have an easy way to loop over our words array, the same as for a string: while(words[i] != 0).

The subject is not that hard to understand, the more complex thing is to your code do all that.

#include "libft.h"

static int count_word(char const *s, char c)
{
int f;
int count_word;
int i;

count_word = 0;
i = 0;
f = 0;
while (s[i])
{
if (s[i] == c)
f = 0;
else if (f == 0)
{
f = 1;
count_word++;
}
i++;
}
return (count_word);
}

static char *ft_create_word(char const *str, char c)
{
char *dest;
int i;

i = 0;
while (str[i] && str[i] != c)
i++;
dest = (char *)malloc(sizeof(char) * i + 1);
if (!dest)
return (NULL);
i = 0;
while (str[i] && str[i] != c)
{
dest[i] = str[i];
i++;
}
dest[i] = '\0';
return (dest);
}

static char **ft_free(char **split, int i)
{
while (--i)
free(split[i]);
free(split);
return (NULL);
}

char **ft_split(char const *str, char c)
{
char **dest;
int i;

if (!str)
return (NULL);
i = 0;
dest = (char **)malloc(sizeof(char *) * (count_word(str, c) + 1));
if (!dest)
return (NULL);
while (*str)
{
while (*str && *str == c)
str++;
if (*str && *str != c)
{
dest[i++] = ft_create_word(str, c);
if (dest[i - 1] == NULL)
return (ft_free(dest, i));
while (*str && *str != c)
str++;
}
}
dest[i] = NULL;
return (dest);
}

ft_itoa

Understandable explanation

The ft_itoa function converts the given integer into string representation. It first determines the sign of the number by checking if it is negative. It calculates the length of the resulting string by counting the number of digits in the absolute value of the integer. Memory is allocated to store the resulting string, including space for the sign and the null-terminator. The conversion is performed by iteratively extracting the digits from the integer using modulo and division operations. The digits are then converted to characters by adding the ASCII value of ‘0’. The digits are added to the string in reverse order to ensure the correct representation of the number. Finally, the sign is added if necessary, the null-terminator is appended, and the resulting string is returned.

#include "libft.h"

static int nbr_len(int nbr)
{
int len;

len = 0;
if (nbr < 1)
len++;
while (nbr)
{
nbr /= 10;
len++;
}
return (len);
}

static long long abs_val(long long n)
{
long long nb;

nb = 1;
if (n < 0)
nb *= -n;
else
nb *= n;
return (nb);
}

static char *str_new(size_t n)
{
char *str;

str = (char *)malloc(sizeof(char) * (n + 1));
if (!str)
return (NULL);
return (str);
}

char *ft_itoa(int n)
{
unsigned int nbr;
int sign;
int len;
char *str;

sign = 0;
if (n < 0)
sign = 1;
len = nbr_len(n);
str = str_new(len);
if (!str)
return (NULL);
*(str + len) = '\0';
nbr = abs_val(n);
while (len--)
{
*(str + len) = 48 + nbr % 10;
nbr /= 10;
}
if (sign)
*(str) = 45;
return (str);
}

ft_strmapi

Understandable explanation

This functions takes two parameters, the first one is a string, and the second one is a function.

What ft_strmapi does is apply the function f to every character of the string s.It passes the index of the character in the string, and the character to the function f.The result of the function f is placed in the new string at index i. At the end, we return the new string resulting of the application of f on every character of the string.

#include "libft.h"

char *ft_strmapi(const char *s, char (*f)(unsigned int, char))
{
unsigned int i;
char *res;

res = malloc((ft_strlen(s) + 1) * sizeof(char));
if (!res)
return (NULL);
i = 0;
while (i < ft_strlen(s))
{
res[i] = (*f)(i, s[i]);
i++;
}
res[i] = 0;
return (res);
}

ft_striteri

Understamdable explanation

ft_striteri works the same way as ft_strmapi does, take a look at the explanation for ft_strmapi and then come back here.

The difference between ft_striteri and ft_strmapi is that ft_striteri doesn't return anything and works directly on the original string.

#include "libft.h"

void ft_striteri(char *s, void (*f)(unsigned int, char*))
{
unsigned int i;

i = 0;
while (s[i])
{
(*f)(i, &s[i]);
i++;
}
}

ft_putchar_fd

Understandable explanation

This one is pretty straight forward, you already know how to write the ft_putchar() function, if you don't remember, look back at what you did during your Piscine.

#include "libft.h"

void ft_putchar_fd(char c, int fd)
{
if(id != -1 )
write(fd, &c, 1);
}

ft_putnbr_fd

Understandable explanation

This function works the same way as the ft_putnbr() function you had to do during the Piscine, it also takes the fdparameter, like ft_putchar_fd(), ft_putstr_fd(), ft_putendl_fd().

#include "libft.h"

void ft_putnbr_fd(int n, int fd)
{
if (fd == -1)
return ;
if (n == -2147483648)
write(fd, "-2147483648", 11);

else if (n < 0)
{
write(fd, "-", 1);
n = -n;
ft_putnbr_fd(n, fd);
}
else
{
if (n > 9)
{
ft_putnbr_fd(n / 10, fd);
ft_putnbr_fd(n % 10, fd);
}
else
{
digit = n + 48;
write(fd, &digit, 1);
}
}
}

Bonus

— — — — — — — — — — — — — — — — — — — — — — — — — — — — —-— — — — — — — — — — — — — — — — — — — — — — — — — — — — —— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

--

--