微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

创建动态数组,但将分段错误视为错误

如何解决创建动态数组,但将分段错误视为错误

我想创建一个包含用户输入的动态数组。但是在我第一次输入后,我不断收到分段错误作为错误。我知道分段错误是由于错误的内存访问引起的。有没有办法定位代码中的错误

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

int main(int argc,char *argv[])
 {
    int length,i;
    int size_arr = 1;
    int size_word = 102;
    char **arr;
    char *word;
    arr = malloc(size_arr * sizeof(char*));
    word = malloc(size_word *sizeof(char));
    if(arr == NULL)
    {
        fprintf(stderr,"No reallocation");
        exit(EXIT_FAILURE);
    }
    else
    {
        while(!feof(stdin) && fgets(word,size_word,stdin) != NULL)
        {   
            arr[i] = strndup(word,size_word);
            length = strlen(word);
            if(length > 102)
            {
                fprintf(stderr,"Word is too long");
            }
            if(size_arr-1 == i)
            {
                size_arr *= 2;
                arr = realloc(arr,size_arr * sizeof(char*));
            }
            i++;
        }
 
    }
    free(arr);
    return 0;
}

您好, 索雷斯

解决方法

您很接近,您只是对如何处理将所有部分放在一起有点困惑。首先,了解您的代码中不涉及数组。当您使用指针到指针(例如char **arr)构建集合时,它是一个两步过程。 arr 本身是一个指向已分配指针块的单指针——每次通过调用 realloc() 重新分配一个额外的指针,将其扩展一个以添加下一个单词。

第二步是为每个单词分配存储空间。然后,您将该字存储的起始地址分配给您分配的指针。

所以你有一个指针块,你可以扩展(realloc())来添加一个指针来保存下一个字的地址,然后你为那个字分配存储空间,将起始地址分配给你的新指针,然后然后将单词复制到该地址。

注意: 每次迭代都调用 realloc() 以仅添加一个指针是低效的。您可以通过添加另一个计数器(例如 allocated)来解决该问题分配的指针和一个计数器 used,以跟踪您使用的指针数量。您只在 used == allocated 时重新分配。这样您就可以,例如,每次将可用的指针数量加倍 --或您选择的任何增长方案)

另请注意,strdup()strndup() 很方便,但不是标准 C 库的一部分(它们是 POSIX)。虽然大多数编译器都会提供它们,但您可能需要正确的编译器选项以确保它们可用。

让我们看一下您的示例,在简单的情况下,仅使用标准库提供的函数。我们会让您按一种方案重新分配,然后让您实施 used == allocated 方案以便稍后清理。

读取数据行时,在读取该行之前您不会知道需要存储多少个字符——因此只需重用相同的固定大小缓冲区来读取每一行。然后您可以修剪 '\n' 包含的 fgets() 并获得您需要分配的字符的长度(+1 用于 *nul 终止字符)。然后简单地分配,分配给您的新指针并从固定缓冲区复制到单词(或行)的新存储块。一个 1K、2K 等的固定缓冲区就可以了。

所以让我们收集代码所需的变量,为固定缓冲区大小定义一个常量,例如

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

#define MAXC 1024       /* if you need a constant,#define one (or more) */

int main (void)
{
    char **arr = NULL,/* pointer to block of allocated pointers */
        buf[MAXC];          /* fixed buffer for read-input */
    int size_arr = 0;       /* only 1 counter needed here */

现在让我们将一行读入 buf 并从分配指针开始:

    while (fgets (buf,MAXC,stdin))
    {   
        size_t len;
        
        /* allocate pointer (one each time rather inefficient) */
        void *tmp = realloc (arr,(size_arr + 1) * sizeof *arr);
        if (!tmp) {                                 /* VALIDATE */
            perror ("realloc-arr");
            break;
        }
        arr = tmp;                                  /* assign on success */

(如我的评论中所述,您永远不会 realloc() 使用指针本身,因为当(不是如果)realloc() 失败时,它将覆盖指针地址(例如指向您的指针集合的地址)与 NULL。)

如上,您realloc()指向临时指针tmp,验证重新分配成功,然后将新分配的指针块分配给arr

现在修剪 '\n' 中的 buf 并获取字符数。 (其中 strcspn() 允许您在一次调用中完成所有操作):

        buf[(len = strcspn (buf,"\n"))] = 0;       /* trim \n,save len */

现在只为 len + 1 个字符分配存储空间,并从 buf 复制到 arr[size_arr]

        arr[size_arr] = malloc (len + 1);           /* allocate for word */
        if (!arr[size_arr]) {                       /* VALIDATE */
            perror ("malloc-arr[i]");
            break;
        }
        memcpy (arr[size_arr],buf,len + 1);       /* copy buf to arr[i] */
        size_arr += 1;                              /* increment counter */
    }

(注意:当每次迭代重新分配 1 个指针时,只需要一个计数器变量,并注意它是如何在指针重新分配和字存储分配得到验证后才增加的,并将单词从 buf 复制到 arr[size_arr]。任一分配失败时,循环都会中断,您的 size_arr 仍将保留正确数量的存储单词)

这样就完成了你的阅读循环。

现在您可以使用您存储的 size_arr 指针集合,每个指针都根据您的需要指向一个已分配和存储的字。但请记住,当需要释放内存时,这也是一个 2 步过程。您必须为每个字释放分配的块,之前释放分配的指针块,例如

     for (int i = 0; i < size_arr; i++) {            /* output result */
        puts (arr[i]);
        free (arr[i]);                              /* free word storage */
    }
    free(arr);                                      /* free pointers */

完成。

完整的程序是:

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

#define MAXC 1024       /* if you need a constant,/* pointer to block of allocated pointers */
        buf[MAXC];          /* fixed buffer for read-input */
    int size_arr = 0;       /* only 1 counter needed here */
    
    while (fgets (buf,(size_arr + 1) * sizeof *arr);
        if (!tmp) {                                 /* VALIDATE */
            perror ("realloc-arr");
            break;
        }
        arr = tmp;                                  /* assign on success */
        
        buf[(len = strcspn (buf,save len */
        
        arr[size_arr] = malloc (len + 1);           /* allocate for word */
        if (!arr[size_arr]) {                       /* VALIDATE */
            perror ("malloc-arr[i]");
            break;
        }
        memcpy (arr[size_arr],len + 1);       /* copy buf to arr[i] */
        size_arr += 1;                              /* increment counter */
    }
    
    for (int i = 0; i < size_arr; i++) {            /* output result */
        puts (arr[i]);
        free (arr[i]);                              /* free word storage */
    }
    free(arr);                                      /* free pointers */
}

示例使用/输出

测试一下。做一些有创意的事情,比如读取、存储和输出你的源文件,以确保它有效,例如

$ ./bin/dynarr < dynarr.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXC 1024       /* if you need a constant,/* pointer to block of allocated pointers */
        buf[MAXC];          /* fixed buffer for read-input */
    int size_arr = 0;       /* only 1 counter needed here */

    while (fgets (buf,stdin))
    {
        size_t len;

        /* allocate pointer (one each time rather inefficient) */
        void *tmp = realloc (arr,(size_arr + 1) * sizeof *arr);
        if (!tmp) {                                 /* VALIDATE */
            perror ("realloc-arr");
            break;
        }
        arr = tmp;                                  /* assign on success */

        buf[(len = strcspn (buf,save len */

        arr[size_arr] = malloc (len + 1);           /* allocate for word */
        if (!arr[size_arr]) {                       /* VALIDATE */
            perror ("malloc-arr[i]");
            break;
        }
        memcpy (arr[size_arr],len + 1);       /* copy buf to arr[i] */
        size_arr += 1;                              /* increment counter */
    }

    for (int i = 0; i < size_arr; i++) {            /* output result */
        puts (arr[i]);
        free (arr[i]);                              /* free word storage */
    }
    free(arr);                                      /* free pointers */
}

内存使用/错误检查

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

您必须使用内存错误检查程序来确保您不会尝试访问内存或超出/超出分配块的范围进行写入,尝试读取或基于未初始化值的条件跳转,最后,以确认您释放了所有分配的内存。

对于 Linux valgrind 是正常选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。

$ valgrind ./bin/dynarr < dynarr.c
==30995== Memcheck,a memory error detector
==30995== Copyright (C) 2002-2017,and GNU GPL'd,by Julian Seward et al.
==30995== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==30995== Command: ./bin/dynarr
==30995==
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

    <snipped code>
}
==30995==
==30995== HEAP SUMMARY:
==30995==     in use at exit: 0 bytes in 0 blocks
==30995==   total heap usage: 84 allocs,84 frees,13,462 bytes allocated
==30995==
==30995== All heap blocks were freed -- no leaks are possible
==30995==
==30995== For counts of detected and suppressed errors,rerun with: -v
==30995== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认您已释放分配的所有内存,并且没有内存错误。

检查一下,如果您还有其他问题,请告诉我。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?