简体   繁体   中英

Segfault in a char ** pointer (learning malloc and pointers usage)

I'm currently learning how pointers and the malloc function work. I'm trying to write a function in C that will take a string as a parameter. Basically, it will store each word of this string in a char **ptr , and will distinct words when finding a space/tabulation/'\n' char. For instance, the string "hello world" will be stored as ptr[0] = "hello , ptr[1] = world .

So far this is what I wrote:

#include "libft.h"
#include <stdlib.h>


char **ft_split_whitespaces(char *str)
{
    int i;
    int j;
    int k;
    char **tab;

    i = 0;
    j = 0;
    k = 0;
    tab = (char **)malloc(sizeof(char) * 8);
    *tab = (char *)malloc(sizeof(char) * 8);
    while(str[i])
    {
        k = 0;
        while(str[i] == 9 || str[i] == 32 || str[i] == 10 || str[i] == '\0') // if a whitespace or a tabulation or a '\n' char is found, we go further in the string and do nothing
        {
            i++;
        }
        while(str[i] != 9 && str[i] != 32 && str[i] != 10 && str[i] != '\0') // if this isn't the case, we put the current char str[i] in the new array
        {
            tab[j][k] = str[i];
            k++;
            i++;
        }
        if(str[i] == 9 || str[i] == 32 || str[i] == 10 || str[i] == '\0') // now if a new whitespace is found we're going to store the next word in tab[j+1]
        {
            j++;
        }
        i++;
    }   
    return (tab);
}


int     main(void)
{
    char str[] = "  hi im   a       new bie  learning malloc\nusage";
    ft_split_whitespaces(str);
}

Note that I've tried to malloc several values but it doesn't seem to change anything: it will segfault when trying to copy the char 'i' of the word 'im' (second word) into my array.

Could you guys please guide me and tell me what am I missing?

Thanks a lot in advance !

This line:

    tab = (char **)malloc(sizeof(char) * 8);

only allocates 8 bytes to store all the pointers. Assuming 8 is the maximum number of pointers, it should be:

    tab = (char **)malloc(sizeof(char *) * 8);

This line:

    *tab = (char *)malloc(sizeof(char) * 8);

allocates space for the first word which can be up to 7 characters long plus a null terminator.

However, that only allocates memory for the first word pointed to by tab[0] . The code does not allocate any memory for the remaining words.

How should ft_split_whitespaces indicate the number of words it split out of the input? One way to do that is to add a null pointer after the pointer to the last word in the list.

Here is a working demo. It uses the strspn and strcspn functions to work its way through the whitespace and non-whitespace portions of the input string. It scans the input string twice - once to determine the number of words and once to allocate and copy the words. It allocates one memory block for the null-terminated list of pointers, and allocates a memory block for each word.

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>

/* Free NULL-terminated list of strings. */
void ft_free_strlist(char **strings)
{
    if (strings)
    {
        char **ss = strings;

        while (*ss)
        {
            free(*ss++);
        }
        free(strings);
    }
}

char **ft_split_whitespaces(const char *input)
{
    static const char white[] = "\t\n\v\f\r ";
    size_t nwords;
    size_t i;
    size_t wordlen;
    char **words;
    const char *s;

    /* Determine number of words in input string. */
    nwords = 0;
    s = input;
    s += strspn(s, white);  /* Skip initial whitespace. */
    while (*s)
    {
        /* Found non-whitespace at beginning of word. */
        nwords++;
        s += strcspn(s, white); /* Skip non-whitespace. */
        s += strspn(s, white);  /* Skip whitespace. */
    }

    /* Allocate memory for NULL-terminated list of words. */
    words = malloc((nwords + 1) * sizeof(words[0]));
    if (words != NULL)
    {
        /* Rescan input, allocate and copy words. */
        s = input;
        for (i = 0; i < nwords; i++)
        {
            s += strspn(s, white);  /* Skip whitespace. */
            wordlen = strcspn(s, white);    /* Determine word length. */
            /* Allocate memory for null-terminated word. */
            words[i] = malloc((wordlen + 1) * sizeof(*s));
            if (words[i] == NULL)
            {
                /* Error will be dealt with later. */
                break;
            }
            /* Copy word (source is not null-terminated). */
            memcpy(words[i], s, wordlen * sizeof(*s));
            words[i][wordlen] = 0;  /* Append null terminator to word. */
            s += wordlen;   /* Skip past the copied word. */
        }
        /* NULL-terminate the list of words. */
        words[i] = NULL;
        if (i < nwords)
        {
            /* Could not allocate enough memory.  Free what we got. */
            ft_free_strlist(words);
            words = NULL;   /* Will return NULL later. */
        }
    }
    if (words == NULL)
    {
        /* There was a memory allocation error. */
        errno = ENOMEM;
    }
    /* Return the NULL-terminated list of words, or NULL on error. */
    return words;
}

/* Print NULL-terminated list of strings. */
void ft_print_strlist_const(const char *const *strings)
{
    if (strings)
    {
        while (*strings)
        {
            puts(*strings);
            strings++;
        }
    }
}

void ft_print_strlist(char *const *strings)
{
    ft_print_strlist_const((const char *const *)strings);
}

int main(void)
{
    static const char str[] = "  hi im   a       new bie  learning malloc\nusage";
    char **words;

    words = ft_split_whitespaces(str);
    if (words == NULL)
    {
        perror("Split whitespace");
        return EXIT_FAILURE;
    }
    ft_print_strlist(words);
    ft_free_strlist(words);
    return EXIT_SUCCESS;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM