创建一个动态数组,但将分段错误作为错误 [英] Creating an dynamic array, but getting segmentation fault as error

查看:14
本文介绍了创建一个动态数组,但将分段错误作为错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

i wanted to create a dynamic array, which will contain user input. But I keep getting segmentation fault as an error after my first input. I know that segmentation fault is caused due false memory access. Is there a way to locate the error in the code ?

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

您好,索雷思

推荐答案

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

You are close, you are just a bit confused on how to handle putting all the pieces together. To begin with, understand there are no arrays involved in your code. When you are building a collection using a pointer-to-pointer (e.g. char **arr), it is a two step process. arr itself is a single-pointer to an allocated block of pointers -- which you expand by one each time to add the next word by calling realloc() to reallocate an additional pointer.

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

The second step is to allocated storage for each word. You then assign the beginning address for that word's storage to the pointer you have allocated.

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

So you have one block of pointers which you expand (realloc()) to add a pointer to hold the address for the next word, you then allocate storage for that word, assigning the beginning address to your new pointer and then copy the word to that address.

(注意: 每次迭代调用 realloc() 只添加一个指针是低效的.您可以通过添加另一个计数器来解决这个问题(例如 allocated) 保存分配的指针数量,以及一个计数器 used,用于跟踪您已使用的指针数量.您仅在 used == assigned 时重新分配. 这样你就可以,例如每次可用的指针数量翻倍——或者你选择的任何增长方案)

(note: calling realloc() every iteration to add just one-pointer is inefficient. You solve that by adding another counter (e.g. allocated) that holds the number of pointers allocated, and a counter used, to keep track of the number of pointers you have used. You only reallocate when used == allocated. That way you can, e.g. double the number of pointers available each time -- or whatever growth scheme you choose)

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

Also note that strdup() and strndup() are handy, but are not part of the standard C library (they are POSIX). While most compilers will provide them, you may need the right compiler option to ensure they are available.

让我们看一下您的示例,在简单的情况下,仅使用标准库提供的函数.我们将让您通过一个方案重新分配,并让您实现 used == allowed 方案以便稍后清理.

Let's look at your example, in the simple case, using only functions provided by the standard library. We will keep you reallocate by one scheme, and leave you to implement the used == allocated scheme to clean that up later.

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

When reading lines of data, you won't know how many characters you need to store until the line is read -- so just reused the same fixed-size buffer to read each line. Then you can trim the ' ' included by fgets() and get the length of the characters you need to allocate (+1 for the *nul-terminating character). Then simply allocate, assign to your new pointer and copy from the fixed buffer to the new block of storage for the word (or line). A 1K, 2K, etc.. fixed buffer is fine.

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

So let's collect the variables you need for your code, defining a constant for the fixed buffer size, e.g.

#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 并从分配指针开始:

Now let's read a line into buf and start by allocating your pointer:

    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.)

(as noted in my comment, you never realloc() using the pointer itself because when (not if) realloc() fails, it will overwrite the pointer address (e.g. the address to your collection of pointers) with NULL.)

所以在上面,你 realloc() 指向临时指针 tmp,验证重新分配成功,然后将新分配的指针块分配给 arr.

So above, you realloc() to the temporary pointer tmp, validate the reallocation succeeded, then assign the newly allocated block of pointers to arr.

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

Now trim the ' ' from buf and get the number of characters. (where strcspn() allows you to do this all in a single call):

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

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

Now just allocated storage for len + 1 characters and copy from buf to 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 仍将保持正确的存储字数)

(note: when reallocating 1 pointer per-iteration, only a single counter variable is needed, and note how it is not incremented until both the pointer reallocation, the allocation for your word storage is validated, and the word is copied from buf to arr[size_arr]. On failure of either allocation, the loop is broken and your size_arr will still hold the correct number of stored words)

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

That completes you read-loop.

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

Now you can use your stored collection of size_arr pointers, each pointing to an allocated and stored word as you wish. But remember, when it comes time to free the memory, that too is a 2-step process. You must free the allocated block for each word, before freeing the block of allocated pointers, e.g.

     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, #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 */
    
    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 */
        
        buf[(len = strcspn (buf, "
"))] = 0;       /* trim 
, 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], buf, 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 */
}

使用/输出示例

测试一下.做一些创造性的事情,比如读取、存储和输出你的源文件,以确保它正常工作,例如

Test it out. Do something creative like read, store and output your source file to make sure it works, e.g.

$ ./bin/dynarr < dynarr.c
#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 */

    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 */

        buf[(len = strcspn (buf, "
"))] = 0;       /* trim 
, 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], buf, 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) 当不再需要它时可以释放.

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

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

It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.

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

For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

$ 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)

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

Always confirm that you have freed all memory you have allocated and that there are no memory errors.

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

Look things over and let me know if you have further questions.

这篇关于创建一个动态数组,但将分段错误作为错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆