malloc 到 "array" 个指针的元素

malloc to an element of an "array" of pointers

那里。

我正在尝试制作一个读取 N 个单词的程序(当您键入 - 时结束)并按顺序打印。

我的问题是:我正在尝试使用某种动态的 char* 数组。它重新分配一个元素,但是当我尝试在堆上创建 space 到字符串(第 21 行)时崩溃。

可以修复吗?谢谢。

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

#define WORD_SIZE 11

int main()
{
        char word[WORD_SIZE];
        char **set;
        int j;
        int numberWord = 0;

        /* input */
        printf("Insert a word, or '-' to stop: ");
        fgets(word, WORD_SIZE, stdin);

        while (strcmp(word, "-\n")) {
                /* process */
                set = realloc(set, sizeof(char *) * (numberWord + 1));
                set[numberWord] = malloc(sizeof(char) * WORD_SIZE);
                numberWord++;

                /* input */
                printf("Insert a word, or '-' to stop: ");
                fgets(word, WORD_SIZE, stdin);
        }

        /* output */
        printf("\nSORTED:\n");
        for (j = 0; j <  numberWord; j++) {
                printf("%s", set[j]);
        }

        printf("\n");
        free(set);

        return 0;
}

realloc 要求重新分配的内存之前已经分配 (see here)。

尝试在循环之前添加 set = malloc(1); 以在手前分配至少 1 个字节。

除了初始化 set 和复制 word 之外,您还遗漏了许多其他稍微微妙的问题,这些问题可能会困扰您。首先让我们从几个无关紧要的逻辑问题开始。很少,如果你真的想存储一个字符串,'\n' 悬在最后,只需删除 fgets 读取并包含在 word 中的 newline,例如

        /* remove trailing '\n' */
        size_t len = strlen (word);
        if (word[len - 1] == '\n')
            word[--len] = 0;            /* overwrite with nul-byte */

(具有 len 还使您能够分配容纳 word 所需的确切内存量,例如 len + 1

此时您还可以通过简单的长度字符比较来检查退出条件,例如

         if (len == 1 && *word == '-')   /* test for '-' */
            break;

(这也意味着您的读取循环可以只是 for (;;) {...},不会将 "-" 存储为您的单词之一)

如我的评论所述,永远不要直接分配正在重新分配的指针的 return。为什么?如果 realloc 失败,则 set 不会被释放并且 realloc returns NULL - 然后您将其分配给 set 丢失对 set 并造成内存泄漏。相反,始终使用临时指针,例如

        /* process - validate all allocations */
        void *tmp = realloc (set, sizeof *set * (numberWord + 1));
        if (!tmp) {
            fprintf (stderr, "error: virtual memory exhausted - realloc.\n");
            break;
        }
        set = tmp;

您正在为每个 word 分配内存,但未能 free 您为每个单词分配的内存。如果此后不再需要内存,则可以在 output 循环中执行此操作,例如

    /* output */
    printf ("\nSORTED:\n");
    for (j = 0; j <  numberWord; j++) {
        printf (" %s\n", set[j]);
        free (set[j]);          /* don't forget to free word */
    }
    free(set);                  /* free pointers */

    putchar ('\n');             /* don't use printf for single char */

上面你表示你是按SORTED顺序输出你的单词,但是你没有对单词进行排序。最简单的单词排序方法是为set中的指针创建一个简单的比较函数,然后将set传递给qsort进行排序,例如

int cmpstrings (const void *a, const void *b) {
    return strcmp (*(char * const *)a, *(char * const *)b);
}

int main (void) {
    ...
    qsort (set, numberWord, sizeof *set, cmpstrings);   /* sort */

将所有部分放在一起并以更简洁的方式重新安排输入请求的逻辑,您可以执行以下操作:

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

#define WORD_SIZE 11

int cmpstrings (const void *a, const void *b) {
    return strcmp (*(char * const *)a, *(char * const *)b);
}

int main (void) {

    char word[WORD_SIZE] = "",
        **set = NULL;
    int j, numberWord = 0;

    for (;;) {

        /* input */
        printf("Insert a word, or '-' to stop: ");
        fgets(word, WORD_SIZE, stdin);

        /* remove trailing '\n' */
        size_t len = strlen (word);
        if (word[len - 1] == '\n')
            word[--len] = 0;            /* overwrite with nul-byte */

        if (len == 1 && *word == '-')   /* test for '-' */
            break;

        /* process - validate all allocations */
        void *tmp = realloc (set, sizeof *set * (numberWord + 1));
        if (!tmp) {
            fprintf (stderr, "error: virtual memory exhausted - realloc.\n");
            break;
        }
        set = tmp;

        set[numberWord] = malloc (sizeof *set[numberWord] * (len + 1));
        if (!set[numberWord]) {
            fprintf (stderr, "error: virtual memory exhausted - malloc.\n");
            break;
        }

        strcpy (set[numberWord], word);
        numberWord++;
    }

    qsort (set, numberWord, sizeof *set, cmpstrings);   /* sort */

    /* output */
    printf ("\nSORTED:\n");
    for (j = 0; j <  numberWord; j++) {
        printf (" %s\n", set[j]);
        free (set[j]);          /* don't forget to free word */
    }
    free(set);                  /* free pointers */

    putchar ('\n');             /* don't use printf for single char */

    return 0;
}

例子Use/Output

$ ./bin/dynwords
Insert a word, or '-' to stop: my
Insert a word, or '-' to stop: dog
Insert a word, or '-' to stop: has
Insert a word, or '-' to stop: fleas
Insert a word, or '-' to stop: the
Insert a word, or '-' to stop: cat
Insert a word, or '-' to stop: doesn't
Insert a word, or '-' to stop: have
Insert a word, or '-' to stop: any
Insert a word, or '-' to stop: -

SORTED:
 any
 cat
 doesn't
 dog
 fleas
 has
 have
 my
 the

在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址,因此,(2) 当不再需要时可以释放

您必须使用内存错误检查程序来确保您没有写入 beyond/outside 您分配的内存块,试图读取或基于未初始化值的条件跳转并最终确认你已经释放了你分配的所有内存。

对于Linux valgrind是正常的选择。每个平台都有类似的内存检查器。它们都很简单易用,只需运行你的程序通过它,例如

$ valgrind ./bin/dynwords
==10572== Memcheck, a memory error detector
==10572== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==10572== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==10572== Command: ./bin/dynwords
==10572==
Insert a word, or '-' to stop: my
Insert a word, or '-' to stop: dog
Insert a word, or '-' to stop: has
Insert a word, or '-' to stop: fleas
Insert a word, or '-' to stop: the
Insert a word, or '-' to stop: cat
Insert a word, or '-' to stop: doesn't
Insert a word, or '-' to stop: have
Insert a word, or '-' to stop: any
Insert a word, or '-' to stop: -

SORTED:
 any
 cat
 doesn't
 dog
 fleas
 has
 have
 my
 the

==10572==
==10572== HEAP SUMMARY:
==10572==     in use at exit: 0 bytes in 0 blocks
==10572==   total heap usage: 18 allocs, 18 frees, 402 bytes allocated
==10572==
==10572== All heap blocks were freed -- no leaks are possible
==10572==
==10572== For counts of detected and suppressed errors, rerun with: -v
==10572== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认您拥有 freed 已分配的所有内存并且没有错误。

检查一下,如果您有任何问题,请告诉我。合并了许多细微的变化,所以如果您有什么不明白的地方,请在下面发表评论。