Building Everlasting Circles and Inventing Wheels within Wheels - kevshouse/The-Under_C_World GitHub Wiki

You

Good morning BB, This morning I would like to rewrite my ft_split() function as my current one is bloated and therefore will not pass norminette. I was thinking that I could use some of the other functions that are in my libft library to handle the tasks.

BLACKBOXAI

Good morning! It sounds like you're looking to refactor your ft_split() function to make it cleaner and more compliant with Norminette. Using helper functions from your libft can definitely help streamline your code. Below is a general approach to rewriting ft_split() while keeping it concise and organized.

Steps to Refactor ft_split() Identify Helper Functions: Determine which functions from your libft can be used. Commonly useful functions might include:

ft_strlen() for string length. ft_strchr() for finding characters in strings. ft_strdup() for duplicating strings. ft_memalloc() or ft_calloc() for memory allocation. Break Down the Logic: The ft_split() function typically involves:

Counting the number of words. Allocating memory for the array of strings. Splitting the input string into words based on delimiters. Handling memory management properly. Implement the Function: Here’s a simplified version of ft_split() using helper functions:


#include "libft.h"

static int count_words(const char *s, char c)
{
    int count = 0;
    int in_word = 0;

    while (*s)
    {
        if (*s != c && !in_word)
        {
            in_word = 1;
            count++;
        }
        else if (*s == c)
        {
            in_word = 0;
        }
        s++;
    }
    return count;
}

static char *get_next_word(const char **s, char c)
{
    const char *start;
    char *word;
    size_t len;

    while (**s == c && **s)
        (*s)++;
    start = *s;
    while (**s && **s != c)
        (*s)++;
    len = *s - start;
    if (len == 0)
        return NULL;
    word = ft_substr(start, 0, len);
    return word;
}

char **ft_split(const char *s, char c)
{
    char **result;
    int word_count;
    int i;

    if (!s)
        return NULL;
    word_count = count_words(s, c);
    result = (char **)ft_calloc(word_count + 1, sizeof(char *));
    if (!result)
        return NULL;
    i = 0;
    while (i < word_count)
    {
        result[i] = get_next_word(&s, c);
        if (!result[i])
        {
            while (i > 0)
                free(result[--i]);
            free(result);
            return NULL;
        }
        i++;
    }
    return result;
}

Explanation of the Code count_words(): This function counts the number of words in the string by checking transitions between delimiters and non-delimiters. get_next_word(): This function extracts the next word from the string, moving the pointer forward and returning a newly allocated string. ft_split(): This is the main function that initializes the result array, counts the words, and fills the array with words using the helper functions. Memory Management Make sure to handle memory properly. If any allocation fails, free any previously allocated memory to avoid leaks.

Testing After refactoring, thoroughly test your ft_split() function with various inputs to ensure it behaves as expected.

Feel free to adjust the code to fit your specific needs or coding style! If you have any specific parts of your original function that you want to keep or modify, let me know!

You

why do you use sizeof(char *) ?

BLACKBOXAI

In the context of allocating memory for an array of strings in C, using sizeof(char *) is important for ensuring that the correct amount of memory is allocated for each pointer in the array.

Explanation Understanding the Array of Strings:

When you split a string into an array of strings (e.g., char **result), you are essentially creating an array where each element is a pointer to a char (i.e., a string). The type of result is char **, which means it is a pointer to a pointer to a char. Memory Allocation:

When you allocate memory for this array, you need to allocate enough space for the number of pointers (each pointing to a string) that you will store in the array. The expression sizeof(char *) gives you the size of a pointer to a char on your platform (which is typically 4 bytes on a 32-bit system and 8 bytes on a 64-bit system). Using ft_calloc:

In the line result = (char **)ft_calloc(word_count + 1, sizeof(char *));, you are allocating memory for word_count + 1 pointers to char. The +1 is included to account for the null terminator (the last element in the array should be NULL to indicate the end of the array). Example If you have word_count equal to 5, and you want to allocate memory for 6 pointers (5 strings plus one NULL terminator), the allocation would look like this:


result = (char **)ft_calloc(6, sizeof(char *));

This ensures that you have enough space to store 6 pointers, each of which can point to a string.

Summary Using sizeof(char *) in the memory allocation ensures that you allocate the correct amount of memory for the array of string pointers, regardless of the architecture (32-bit vs. 64-bit) on which your program is running. This is a good practice in C programming to ensure portability and correctness.

You

Please step me through the execution of getnext_word().

BLACKBOXAI

Sure! Let's step through the execution of the get_next_word() function in detail. This function is designed to extract the next word from a string, given a pointer to the string and a delimiter character. Here’s the function again for reference:


static char *get_next_word(const char **s, char c)
{
    const char *start;
    char *word;
    size_t len;

    while (**s == c && **s)
        (*s)++;
    start = *s;
    while (**s && **s != c)
        (*s)++;
    len = *s - start;
    if (len == 0)
        return NULL;
    word = ft_substr(start, 0, len);
    return word;
}

Parameters const char **s: A pointer to a pointer to a character. This allows the function to modify the original string pointer, moving it forward as it processes the string. char c: The delimiter character used to separate words. Execution Steps Skip Leading Delimiters:


while (**s == c && **s)
    (*s)++;

This loop checks if the current character pointed to by *s is equal to the delimiter c and if it is not the null terminator (\0). If it is a delimiter, it increments the pointer *s to skip over it. This continues until it finds a non-delimiter character or reaches the end of the string. Mark the Start of the Word:


start = *s;

After skipping the delimiters, start is set to the current position of *s, which is now pointing to the first character of the next word. Find the End of the Word:


while (**s && **s != c)
    (*s)++;

This loop continues to increment *s until it reaches either the end of the string (\0) or the next delimiter c. At this point, *s will be pointing to the character immediately after the last character of the word. Calculate the Length of the Word:



len = *s - start;

The length of the word is calculated by subtracting the start pointer from the current position of *s. This gives the number of characters in the word. Check for Empty Words:


if (len == 0)
    return NULL;

If the length of the word is zero (which can happen if there are consecutive delimiters), the function returns NULL, indicating that there is no valid word to return. Extract the Word:


word = ft_substr(start, 0, len);

If the length is greater than zero, the function calls ft_substr() to create a new string that contains the word. ft_substr() takes the starting position (start), an offset of 0, and the length of the word (len). Return the Word:

return word;

Finally, the function returns the newly created string containing the word. Example Execution Let's say we have the following string and delimiter:


const char *str = "   Hello, World!   ";
const char delimiter = ' ';
const char **s = &str; // Pointer to the string 

Initial State: *s points to the first character (' '). Skip Leading Delimiters: The first loop skips the spaces, so *s now points to 'H'. Start of the Word: start is set to point to 'H'. Find the End of the Word: The second loop increments *s until it reaches the space after '!', so *s now points to the space after '!'. Calculate Length: len is calculated as the difference between the current *s and start, which gives the length of the word "Hello," (7 characters). Check for Empty Word: Since len is not zero, it proceeds to extract the word. Extract the Word: ft_substr(start, 0, len) creates a new string containing "Hello,". Return the Word: The function returns the string "Hello,". This process allows get_next_word() to effectively extract words from a string while handling leading and trailing delimiters. If you have any further questions or need clarification on any part, feel

You

To pass the first stage of libftTester, I had to initialise len and start variables of the get_nect_word() to 0. However, it now fails on "ft_split : 1.MKO ==227695== Invalid read of size 8 ==227695== at 0x4049AC: main (ft_split_test.cpp:29) ==227695== Address 0x0 is not stack'd, malloc'd or (recently) free'd ==227695== 2.SIGSEGV "

BLACKBOXAI

address sanitizer heap use after free - Stack Overflow

stackoverflow The "Invalid read of size 8" error typically indicates that your code is trying to access memory that has already been freed or is otherwise invalid. In the context of the ft_split function, this could happen if you're using pointers incorrectly or not managing memory properly. To fix this, ensure

You

please carry on.

BLACKBOXAI

Certainly! Let's address the potential issues that could lead to the "Invalid read of size 8" error in your get_next_word() function and the overall ft_split() implementation.

Common Causes of Invalid Read Errors Dereferencing a NULL Pointer: If *s is NULL or points to an invalid memory location, dereferencing it will lead to an invalid read. Ensure that the pointer is valid before dereferencing.

Incorrect Pointer Management: If you modify the pointer *s incorrectly, it may point to an invalid location after the function returns. Make sure that the pointer is correctly managed throughout the function.

Memory Management Issues: If you allocate memory for strings but do not manage it properly (e.g., freeing memory that is still in use), it can lead to segmentation faults.

Suggested Modifications Here’s a revised version of the get_next_word() function, ensuring that we handle pointers and memory correctly:


static char *get_next_word(const char **s, char c)
{
    const char *start;
    char *word;
    size_t len = 0; // Initialize len to 0

    // Skip leading delimiters
    while (**s == c && **s)
        (*s)++;

    // If we reached the end of the string, return NULL
    if (**s == '\0')
        return NULL;

    start = *s; // Mark the start of the word

    // Find the end of the word
    while (**s && **s != c)
        (*s)++;

    len = *s - start; // Calculate the length of the word

    // If the length is 0, return NULL (this should not happen due to previous checks)
    if (len == 0)
        return NULL;

    // Create a new string for the word
    word = ft_substr(start, 0, len);
    return word;
}

Key Changes and Checks Initialization of len: The variable len is initialized to 0. This is a good practice, but it should not affect the logic since we calculate len based on the difference between pointers.

Check for End of String: After skipping leading delimiters, we check if we have reached the end of the string (\0). If so, we return NULL immediately. This prevents dereferencing a NULL pointer later.

Memory Allocation: Ensure that ft_substr() is correctly implemented and that it allocates memory properly. If ft_substr() fails to allocate memory, it should return NULL, and you should handle that case in ft_split().

Example of ft_split() Handling Make sure your ft_split() function handles the return value of get_next_word() correctly:


char **ft_split(const char *s, char c)
{
    char **result;
    int word_count;
    int i;

    if (!s)
        return NULL;

    word_count = count_words(s, c);
    result = (char **)ft_calloc(word_count + 1, sizeof(char *));
    if (!result)
        return NULL;

    i = 0;
    while (i < word_count)
    {
        result[i] = get_next_word(&s, c);
        if (!result[i]) // Check if get_next_word returned NULL
        {
            while (i > 0)
                free(result[--i]);
            free(result);
            return NULL;
        }
        i++;
    }
    return result;
}

Additional Debugging Steps Valgrind: Use Valgrind to check for memory leaks and invalid memory accesses. It can help you identify where the invalid read occurs.

Print Debugging: Add print statements to check the values of pointers and lengths at various stages in your functions. This can help you trace where things might be going wrong.

Test Cases: Create a variety of test cases, including edge cases like empty strings, strings with only delimiters, and strings with multiple consecutive delimiters.

By following these guidelines and ensuring proper pointer management and memory allocation, you should be able to resolve the segmentation fault and invalid read errors. If you continue to experience issues, please provide more context or specific test cases that are causing the problem, and I can help you troubleshoot further!

You

I am still getting "ft_split : 1.MKO ==233749== Invalid read of size 8 ==233749== at 0x4049AC: main (ft_split_test.cpp:29) ==233749== Address 0x0 is not stack'd, malloc'd or (recently) free'd ==233749== 2.SIGSEGV " My version of the code is

"#include "libft.h"

static int word_count(const char *s, char c) { int count; int in_word;


count = 0;
in_word = 0;
while (*s)
{
	if (*s != c && !in_word)
	{
		in_word = 1;
		count++;
	}
	else if (*s == c)
	{
		in_word = 0;
	}
	s++;
}
return (count);
}

static char *get_next_word(const char **s, char c) { const char *start_marker; char *word; size_t len;

len = 0;
start_marker = 0;
while (**s == c && **s)
	(*s)++;
if (**s == '\0')
	return (NULL);
start_marker = *s;
while (**s && **s != c)
	(*s)++;
len = *s - start_marker;
if (len == 0)
	return (NULL);
word = ft_substr(start_marker, 0, len);
return (word);
}

char **ft_split(const char *s, char c) { char **result; int numb_words; int i;

numb_words = word_count(s, c);
result = (char **)ft_calloc(numb_words + 1, sizeof(char *));
if (!s)
	return (NULL);
i = 0;
while (1 < numb_words)
{
	result[i] = get_next_word(&s, c);
	if (!result[i])
	{
		while (i > 0)
			free(result[--i]);
		free(result);
		return (NULL);
	}
	i++;
}
return (result);
}"

BLACKBOXAI

Thank you for sharing your code. Let's go through it step by step to identify the issues that might be causing the "Invalid read of size 8" error and the segmentation fault (SIGSEGV).

Issues in Your Code Incorrect Condition in the Loop: In your ft_split() function, the loop condition is incorrect:


while (1 < numb_words)

This condition will always evaluate to true (since 1 < numb_words is not a valid loop condition). It should be:


while (i < numb_words)

Check for NULL Pointer: The check for the input string s should be done before you call word_count() and allocate memory. If s is NULL, you should return NULL immediately:


if (!s)
    return (NULL);

Memory Management: Ensure that ft_substr() is implemented correctly and that it allocates memory properly. If it fails to allocate memory, it should return NULL, and you should handle that case in ft_split().

Initialization of start_marker: You initialize start_marker to 0, which is not necessary. You can simply declare it without initialization, as it will be assigned a valid value later.

Revised Code Here’s a revised version of your code with the necessary corrections:

#include "libft.h"

static int	word_count(const char *s, char c)
{
	int	count;
	int	in_word;

	count = 0;
	in_word = 0;
	while (*s)
	{
		if (*s != c && !in_word)
		{
			in_word = 1;
			count++;
		}
		else if (*s == c)
		{
			in_word = 0;
		}
		s++;
	}
	return (count);
}

static char	*get_next_word(const char **s, char c)
{
	const char	*start_marker;
	char		*word;
	size_t		len;

	len = 0;
	start_marker = NULL; // Initialize to NULL
	while (**s == c && **s)
		(*s)++;
	if (**s == '\0')
		return (NULL);
	start_marker = *s;
	while (**s && **s != c)
		(*s)++;
	len = *s - start_marker;
	if (len == 0)
		return (NULL);
	word = ft_substr(start_marker, 0, len);
	return (word);
}

char	**ft_split(const char *s, char c)
{
	char	**result;
	int		numb_words;
	int		i;

	if (!s) // Check for NULL input string
		return (NULL);
	numb_words = word_count(s, c);
	result = (char **)ft_calloc(numb_words + 1, sizeof(char *));
	if (!result) // Check if memory allocation was successful
		return (NULL);
	i = 0;
	while (i < numb_words) // Correct loop condition
	{
		result[i] = get_next_word(&s, c);
		if (!result[i])
		{
			while (i > 0)
				free(result[--i]);
			free(result);
			return (NULL);
		}
		i++;
	}
	return (result);
}

Key Changes Made Corrected Loop Condition: Changed while (1 < numb_words) to while (i < numb_words) in ft_split(). NULL Check for Input String: Added a check for s before counting words and allocating memory. Memory Allocation Check: Added a check to ensure that result is not NULL after allocation. Initialization of start_marker: Changed start_marker = 0; to start_marker = NULL; for clarity. Additional Debugging Steps Valgrind: Continue using Valgrind to check for memory issues. It will help you identify if there are any memory leaks or invalid accesses. Test Cases: Test your ft_split() function with various inputs, including: An empty string. A string with only delimiters. A string with multiple consecutive delimiters. A normal string with words separated by the delimiter. By making these changes and following the debugging steps, you should be able to resolve the segmentation fault and invalid read errors. If you still encounter issues, please provide the specific test case that causes the error, and we can investigate further!