【问题标题】:Segfault in a char ** pointer (learning malloc and pointers usage)char ** 指针中的段错误(学习 malloc 和指针的用法)
【发布时间】:2021-05-16 12:34:24
【问题描述】:

我目前正在学习指针和malloc 函数的工作原理。 我正在尝试用 C 语言编写一个将字符串作为参数的函数。基本上,它将这个字符串的每个单词存储在char **ptr 中,并且在找到空格/制表/'\n' 字符时会区分单词。 例如,字符串“hello world”将存储为ptr[0] = "helloptr[1] = world

到目前为止,这是我写的:

#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);
}

请注意,我尝试 malloc 几个值,但它似乎没有改变任何东西:当尝试将单词“im”(第二个单词)的字符“i”复制到我的数组中时,它会出现段错误。

你们能指导我并告诉我我错过了什么吗?

提前非常感谢!

【问题讨论】:

  • 使用isspace()而不是与数字比较。

标签: c pointers segmentation-fault malloc


【解决方案1】:

这一行:

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

只分配 8 个字节来存储所有的指针。假设 8 是最大指针数,应该是:

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

这一行:

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

为第一个单词分配空间,最长可包含 7 个字符,外加一个空终止符。

但是,这只会为tab[0] 指向的第一个字分配内存。代码不会为剩余的单词分配任何内存。

ft_split_whitespaces 应该如何表示它从输入中拆分出来的单词数?一种方法是在指向列表中最后一个单词的指针之后添加一个空指针。

这是一个工作演示。它使用strspnstrcspn 函数来处理输入字符串的空白和非空白部分。它扫描输入字符串两次——一次确定字数,一次分配和复制字。它为空终止的指针列表分配一个内存块,并为每个字分配一个内存块。

#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;
}

【讨论】:

  • 嘿,首先感谢您的回答!由于我对指针也很不熟悉,所以我对你写的一些行有几个问题:/* Allocate memory for NULL-terminated list of words. */ words = malloc((nwords + 1) * sizeof(words[0])); 你为什么写 words[0] 而不是 *words?除此之外。 char *pointer 的大小不会根据它所持有的字符串的长度而改变吗?
  • words[i] = malloc((wordlen + 1) * sizeof(*s)); 你能写words[i] = malloc((worldlen + 1) * sizeof(*char)); 吗?
  • @badakzz 我本可以写words[i] = malloc((worldlen + 1) * sizeof(char));。实际上,* sizeof(char) 不是必需的,因为根据定义,sizeof(char) 始终为 1。我把sizeof 的东西留在那里,以防代码更改为使用宽字符。
  • s = input; 曾经有人告诉我这个命令会复制指针,所以它们仍然会共享同一个地址。显然我一定不明白告诉我的内容,因为地址不一样。是什么让他们共享相同的地址?
  • 我可以写sizeof(*words) 而不是sizeof(words[0])。它们实际上是等效的。
猜你喜欢
  • 2017-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多