从malloc获取seg错误传递了双指针。对语法感到困惑

时间:2017-07-14 20:28:02

标签: c pointers

我只有约2周的C课程介绍,所以我仍然在理解很多这个。我可能不理解技术答案,但我很欣赏任何见解。此外,我们还没有学到很多内容,但是如此先进的技术将会超出我的想象。

我正在编写一个程序,它将从文本文件中读取并将字符存储到双指针"数组" (因为没有更好的术语)。它基本上存储了MxM字搜索拼图。

拼图永远不会超过50x50所以我从malloc第一行开始到50个字符,存储第一行以确定列数,这样我就可以重新分配"行和列的数量"到适当的大小并存储其余的拼图。

在do / while进行第二次迭代后,我遇到了分段错误。我认为我的语法可能是我的前两个malloc命令之一(使用传递的双指针让我感到困惑)。另外,我知道我还没有添加代码来处理失败的malloc。

如果有人能指导我朝着正确的方向前进,我将非常感激。提前致谢。

这是我到目前为止所做的:

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

void storePuzzle(char***);

int main()
{
    char **arr;

    storePuzzle(&arr);
    return 0;
}

void storePuzzle(char ***arr)
{
    int count, count2, size = 0;
    int c;

    *arr = (char**)malloc(sizeof(char*));
    *arr[0] = (char*)malloc(sizeof(char)*50);

    /*determine size of wordsearch and fill first row*/
    do
    {
        c = getchar();
        if(isalpha(c))
        {
            *arr[0][size] = c;
            size++;
        }
    } while(c  != '\n');

    /*realloc row/columns*/
    *arr = (char**)realloc(*arr, sizeof(char*)*size);

    for(count = 0; count < size; count++)
    {   
        *arr[count] = (char*)realloc(*arr[count], sizeof(char)*size);
    }
}

4 个答案:

答案 0 :(得分:1)

好的,这里有一些提示:

char **arr;

storePuzzle(&arr);
return 0;

在上面的代码块中,您没有初始化arr,之后不使用结果。因此,没有理由存在该参数。如果您打算在之后使用该值,则可以让storePuzzle返回指向您的指针。

*arr[0]

[]的优先级高于*。另外* arr和arr [0]基本上做同样的事情,所以当你使用0而不是任何其他数字时这种方法有效。

*arr[count] = (char*)realloc(*arr[count], sizeof(char)*size);

您正在重新分配从未分配过的指针。请记住,您为arr [0]分配了内存,但没有分配其他内存。如果你想采用这种方法,你可以重新分配(* arr)[0],但其他人需要使用malloc:

(*arr)[count] = (char*)malloc(sizeof(char)*size);

答案 1 :(得分:1)

以下将指导一些分配问题,但不是所有OP代码的修复。

分配内存时,请考虑以下模式:

object_pointer = /* no cast needed */ malloc(sizeof *object_pointer * number_needed);
if (object_pointer == NULL) OutOfMemory();

重新分配时:

void_pointer = /* no cast needed */ realloc(object_pointer, 
    sizeof *object_pointer * new_number_needed);
if (void_pointer == NULL) {
  Handle Out of memory,  original object_pointer and its size still valid.
  maybe exit with error message
} 
object_pointer = /* cast not needed */ void_pointer 

这对storePuzzle(char ***arr)有什么影响?

// Avoid magic numbers like 50 littering code
#define PUZZLE_N 50

void storePuzzle(char ***arr) {
    *arr = malloc(sizeof *(*arr));
    if (*arr == NULL) return;

    *arr[0] = malloc(sizeof *(*arr[0]) * PUZZLE_N);
    if (*arr[0] == NULL) {
      free(*arr);
      return;
    }

    /*determine size of wordsearch and fill first row*/
    size_t size = 0;
    int c;
    do {
        c = getchar();
        if (isalpha(c)) {
            *arr[0][size] = c;
            size++;
        }
    } while(c != '\n' && c != EOF && size < PUZZLE_N);

    // Did you want a null character at the end?
    *arr[0][size++] = '\0';

    /* realloc row/columns */
    void *p = realloc(*arr, sizeof *(*arr) * size);
    if (p == NULL) Handle_OOM(); // details omitted
    *arr = p;

    for(size_t count = 0; count < size; count++) {   
      void *p = realloc(*arr[count], sizeof *(*arr[count]) * size);
      if (p == NULL) Handle_OOM(); // details omitted
      *arr[count] = p;
    }
}

答案 2 :(得分:0)

考虑将您的一些功能整合到更小的模块中,以便在遇到困难时更轻松地进行调试。例如,当你需要创建一个缓冲区数组(即char **数组)时,专用一个函数来做到这一点。例如:

char ** Create2D(int lines, int lengths)
{
    int i;
    char **arr = NULL;
    arr = calloc(lines, sizeof(*arr));//creates lines pointers, one for each row of data.
    if(arr)
    {
        for(i=0;i<lines;i++)
        {
            arr[i] = calloc(lengths, 1);//creates space for content in each row.
        }
    }
    return arr;
}

请注意,在调用calloc时,返回的内容不是 as this is not recommended in C
使用与此处所示相同的方法(并且更深入一级)可以编写一个函数来容纳三重指针char ***array,尽管在某种程度上可以编写您的程序,这样就不需要了。请参阅这些关于 a three star programmer 的评论。

这个2D函数将被调用如下:

int main(void)
{
    int i;
    char **arr = Create2D(10, 80);//creates 10 pointers each having room for 80 characters
    if(arr)
    {
        storePuzzle(&arr);//pre-allocated buffer should simplify storePuzzle function
        Free2D(arr, 10); //defined below, frees all memory allocated above.
    }
    return 0;
}

对于每个动态内存分配,都需要有一个相应的免费调用:

void Free2D(char **arr, int lines)
{
    int i;
    for(i=0;i<lines;i++)
    {
        free(arr[i]);
    }
    free(arr);
}

答案 3 :(得分:0)

第一个指针数组是C中两个完全独立的类型。虽然您可以使用指针访问存储在数组中的值,但您可以使用指向的指针的指针建模2D数组,指向指针的指针本身不是array

声明数组,并且仅使用括号声明,例如type name[x];。此处name是一个单维数组x元素类型为type。 (例如,对于x整数,它将是int name[x];。(复合文字初始化的讨论将留待以后使用)

您还可以将指针定义为包含x整数的内存块,例如: int *namep = name;其中整数指针 namep现在指向name中起始元素的地址。

您还可以自行分配该内存块,以便为x整数创建存储空间,例如: int *namep = malloc (x * sizeof (int));(首选等效项为int *namep = (x * sizeof *namep);)此处namep指向内存块的起始地址,其大小足以容纳x整数,就像{{1}但确实存在重要差异。

int name[x](例如sizeof (an array type)返回数组中的字节数。但是sizeof (name)(例如sizeof (a pointer) - 或只是sizeof (namep))只返回 - sizeof namep 指针 - 在x86上通常为sizeof,在x86_64上为4-bytes

当作为参数传递给函数时,第一级数组间接转换为指针。例如,如果您将8-bytes作为参数传递给函数,例如功能

name

您可以将void print_array (int *array, size_t size) { size_t i; for (i = 0; i < size; i++) printf ("array[%2zu] : %d\n", i, array[i]); } 作为name传递给该函数。这里,数组print_array (name, x)在传递给name[x]时会自动转换为指向其第一个元素的指针。由于print_array已经是指针,因此不会进行转换,因此您只需namep即可进行转换。为什么转换很重要?

如果您按照以下方式修改了函数并将print_array (namep, x)传递给它,会发生什么?:

name

void print_array (int *array) { int i, n = (int)(sizeof array / sizeof *array); for (i = 0; i < n; i++) printf ("array[%2zu] : %d\n", i, array[i]); } 实际上会保留元素的数量吗? (答案:)。回想一下,当数组n作为参数传递时,它被转换为指针。所以在这个不正确的版本name只是sizeof arraysizeof (a pointer)只是sizeof *array,其中x86上的结果为sizeof (int) x86_64(1)上的{1/1)或2

现在让我们转向你的代码 - 为什么你开始使用指向char 的指针而不是2D数组令人困惑,但它没有任何问题。但是,不要让一个三星级程序员(通常不是补充),让我们利用函数返回类型来做同样的事情并避免可疑的区别。接下来我们将使用一个简单的2D数组示例。 (注意:在下面的示例中使用指针遍历输入缓冲区有意改变,并使用后面的2D数组示例中的数组索引来演示使用指针算法数组索引是等效的)

2/1

示例输入文件

50行50个随机字符:

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

#define ARSZ 50     /* don't use magic numbers in code, define constants */

char **storepuzzle (int row, int col);

int main (void) {

    char **arr = NULL;

    if (!(arr = storepuzzle (ARSZ, ARSZ))) {  /* validate successful fill */
        fprintf (stderr, "error: storepuzzle failure.\n");
        return 1;
    }

    for (int i = 0; i < ARSZ; i++) {
        for (int j = 0; j < ARSZ; j++)
            printf (" %c", arr[i][j]);
        putchar ('\n');
        free (arr[i]);  /* don't forget to free rows when done with them */
    }

    free (arr);         /* free pointers */

    return 0;
}

char **storepuzzle (int row, int col)
{
    char **tmp = NULL,
        buf[ARSZ + 2] = ""; /* buf to read each line into */
    int ridx = 0;           /* row index */

    tmp = calloc (row, sizeof *tmp);    /* allocate row pointers to char * */
    if (!tmp) {
        fprintf (stderr, "error: memory exhausted.\n");
        return NULL;
    }

    while (fgets (buf, sizeof buf, stdin))
    {
        char *p = buf;      /* pointer to 1st char in buf */
        int cidx = 0;       /* column index */
        tmp[ridx] = calloc (col, sizeof *tmp[ridx]);    /* allocate col chars for row */

        if (!tmp[ridx]) {
            fprintf (stderr, "error: memory exhausted at tmp[%d].\n", ridx);
            exit (EXIT_FAILURE);
        }

        while (*p && *p != '\n')    /* copy each char to column */
            tmp[ridx][cidx++] = *p++;

        if (cidx != col)    /* validate col columns filled */
            fprintf (stderr, "warning: insuffient input for row[%d].\n", ridx);

        ridx++;
    }

    if (ridx != row) {  /* validate row rows filled */
        fprintf (stderr, "error: insufficient number of rows filled.\n");
        for (int i = 0; i < ridx; i++)
            free (tmp[i]);
        free (tmp);
    }

    return tmp;
}

示例使用/输出

$ cat dat/arr50x50.txt
jjnicknlocbvgnpzfbvbbwlfvoobyjqhkehmoupvprqvwfmcga
vhwheknsldtukdykpmefhlopgkulealszzzvjennighkjfuzjr
<snip>
hfcbxnhqooijevomkwzbudzbdwtsfimnooodbnuitcryqxkauj
ugethhibrnbeahkolebfmvhvlxsnqewklavkzddjrxfjepqptr

内存使用/错误检查

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

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

对于Linux $ ./bin/arr50x50 <dat/arr50x50.txt j j n i c k n l o c b v g n p z f b v b b w l f v o o b y j q h k e h m o u p v p r q v w f m c g a v h w h e k n s l d t u k d y k p m e f h l o p g k u l e a l s z z z v j e n n i g h k j f u z j r <snip> h f c b x n h q o o i j e v o m k w z b u d z b d w t s f i m n o o o d b n u i t c r y q x k a u j u g e t h h i b r n b e a h k o l e b f m v h v l x s n q e w k l a v k z d d j r x f j e p q p t r 是正常的选择。每个平台都有类似的记忆检查器。它们都很简单易用,只需通过它运行程序即可。

valgrind

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

使用静态声明的2D数组

这里,您可以简单地返回一个整数值,其中$ valgrind ./bin/arr50x50 <dat/arr50x50.txt ==21813== Memcheck, a memory error detector ==21813== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al. ==21813== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info ==21813== Command: ./bin/arr50x50 ==21813== j j n i c k n l o c b v g n p z f b v b b w l f v o o b y j q h k e h m o u p v p r q v w f m c g a v h w h e k n s l d t u k d y k p m e f h l o p g k u l e a l s z z z v j e n n i g h k j f u z j r <snip> h f c b x n h q o o i j e v o m k w z b u d z b d w t s f i m n o o o d b n u i t c r y q x k a u j u g e t h h i b r n b e a h k o l e b f m v h v l x s n q e w k l a v k z d d j r x f j e p q p t r ==21813== ==21813== HEAP SUMMARY: ==21813== in use at exit: 0 bytes in 0 blocks ==21813== total heap usage: 51 allocs, 51 frees, 2,900 bytes allocated ==21813== ==21813== All heap blocks were freed -- no leaks are possible ==21813== ==21813== For counts of detected and suppressed errors, rerun with: -v ==21813== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) 表示失败,任何其他值表示成功/失败,而不是返回指示成功/失败的指针成功,例如

0

使用/输出相同 - 无需验证内存使用

除非您需要或想要动态声明指向char 指针,否则使用2D数组可以简化操作。您可以通过将#include <stdio.h> #include <stdlib.h> #define ARSZ 50 /* don't use magic numbers in code, define constants */ int storepuzzle (char (*array)[ARSZ], int row, int col); int main (void) { char arr[ARSZ][ARSZ] = {{0}}; /* actual 2D array initialized to zeros */ if (!(storepuzzle (arr, ARSZ, ARSZ))) { /* validate successful fill */ fprintf (stderr, "error: storepuzzle failure.\n"); return 1; } for (int i = 0; i < ARSZ; i++) { for (int j = 0; j < ARSZ; j++) printf (" %c", arr[i][j]); putchar ('\n'); } return 0; } int storepuzzle (char (*array)[ARSZ], int row, int col) { char buf[ARSZ + 2] = ""; /* buf to read each line into */ int ridx = 0; /* row index */ while (fgets (buf, sizeof buf, stdin)) { int cidx = 0; /* column index */ for (int i = 0; buf[i] && buf[i] != '\n'; i++, cidx++) array[ridx][cidx] = buf[i]; /* copy each char to column */ if (cidx != col) { /* validate col columns filled */ fprintf (stderr, "warning: insuffient input for row[%d].\n", ridx); return 0; /* return 0, indicating failure */ } ridx++; } if (ridx != row) { /* validate row rows filled */ fprintf (stderr, "error: insufficient number of rows filled.\n"); return 0; /* return failure */ } return ridx; } 声明为指向char [50] 数组的指针来混合两者进行动态分配,但那是另一天......

仔细检查,了解这两种方法,如果您有任何问题,请告诉我。你原来的罪未能通过arr而不是*arr[0][size] = c;来理解C-Operator的优先级,但是因为你想避免成为 3星级程序员,所以留下来你作为一个练习。