使用指针指向struct时的C分段错误

时间:2017-05-20 18:20:57

标签: c pointers malloc

我已经定义了一个结构StructA和一个函数StructA createStructA(...),它创建并返回一个新的StructA。我希望有一个函数foo(),它将明确返回int错误代码以及我想在StructA之外使用的新foo()。所以我似乎需要指针指针:

int foo(StructA **pA) {
  // Allocate some memory:
  *pA = malloc(sizeof(StructA)); // (Editor's note: don't cast the return of malloc.)

  // check that malloc() didn't error.
  assert(*pA);

  // Fill out a new StructA with createStructA():
  StructA a = createStructA(...);

  // return
  *pA = &a;
  return 0;
}

为什么此代码会出现分段错误?它似乎是由于malloc,好像我注释掉除了malloc线以外的所有其他线,它仍然与segfault断开

由于上面的代码可能看起来不清楚,这是我的问题的MCVE:

#include <stdio.h>
#include <malloc.h>

typedef struct {
  int x;
  int y;
} A;

typedef struct {
  A a;
  int z;
} B;

A makeA() {
  return (A) {.x = 1, .y = 2};
}

B makeB(A a1) {
  return (B) {.a = a1, .z = 3};
}

void parseA(A **ppa) {
  *ppa = malloc(sizeof(A)); // crashes with segfault here
  **ppa = makeA();
}

void parseB(B **ppb) {
  A **ppa;
  parseA(ppa);
  // todo make B .. but it's already crashing
}

int main() {
  B **ppb;
  parseB(ppb);

  return 0;
}

2 个答案:

答案 0 :(得分:3)

*pA = &a设置* pA指向函数foo中的局部变量。当函数返回时,局部变量超出范围,因此* pA变为无效。

修改 只需阅读您的新代码即可。在这一行*ppa = malloc(sizeof(A)); // crashes with segfault here中,ppa还不是有效指针,你不能用* ppa取消引用它。首先需要ppa = (A**)malloc(sizeof(A*));

实际上,我在这里猜测,这是你想要的:



    void parseA(A **ppa) {
        *ppa = (A*)malloc(sizeof(A)); 
        // todo make A, e.g. ppa->x = 1;
    }

    void parseB(B **ppb) {
        A *ppa = NULL;
        parseA(&ppa);
        // ppa is a valid point now, you can do, e.g. ppa->y=1;
        // todo make B
    }

答案 1 :(得分:2)

除了其他更正之外,如果您在调用函数ppa中需要main,则无法将值返回main。虽然你正在做的事情有运动价值,但你的实际目标不过是明确的。它有点像 XY问题,请参阅:What is the XY problem?

也就是说,在行之间阅读,并在ppa中提供main,您的parseB必须以某种方式返回一个值。您可以将type更改为A *parseB (B **ppb)

此外,您似乎对是否将ppappb声明为指针指针指向指针类型感到困惑。鉴于您的初始化和使用,您似乎需要ppappb指针。然后,您将地址传递给函数parseBparseA并相应地取消引用,以便为其内容分配存储空间。这样做,你可以制作一个parseB类似于:

A *parseB (B **ppb)
{
    A *ppa = NULL;
    void *tmp = realloc (*ppb, sizeof **ppb);
    if (!tmp) {
        fprintf (stderr, "error: realloc ppb.\n");
        return NULL;
    }
    *ppb = tmp;

    parseA (&ppa);

    if (ppa)
        **ppb = makeB (*ppa);

    return ppa;
}

注意: realloc用于分配,因为您无法控制先前是否已将*ppb分配给parseB本身 (您可以控制从parseA发送到parseB的内容,以便在malloc parseAmain正常

要启动此菊花链分配和值的分配,您的int main () { B *ppb = NULL; A *ppa = parseB (&ppb); if (ppa && ppb) { printf ("ppa->x: %d\nppa->y: %d\n\n" "ppb->a.x: %d\nppb->a.y: %d\nppb->z: %d\n", ppa->x, ppa->y, ppb->a.x, ppb->a.y, ppb->z); free (ppa); free (ppb); } return 0; } 可以类似于:

#include <stdio.h>
#include <malloc.h>

typedef struct {
    int x;
    int y;
} A;

typedef struct {
    A a;
    int z;
} B;

A makeA ()
{
    return (A) {.x = 1,.y = 2};
}

B makeB (A a1)
{
    return (B) {.a = a1,.z = 3};
}

void parseA (A **ppa)
{
    *ppa = malloc (sizeof (A));
    **ppa = makeA ();
}

A *parseB (B **ppb)
{
    A *ppa = NULL;
    void *tmp = realloc (*ppb, sizeof **ppb);
    if (!tmp) {
        fprintf (stderr, "error: realloc ppb.\n");
        return NULL;
    }
    *ppb = tmp;

    parseA (&ppa);

    if (ppa)
        **ppb = makeB (*ppa);

    return ppa;
}

int main ()
{
    B *ppb = NULL; 
    A *ppa = parseB (&ppb);

    if (ppa && ppb) {
        printf ("ppa->x: %d\nppa->y: %d\n\n"
                "ppb->a.x: %d\nppb->a.y: %d\nppb->z: %d\n",
                ppa->x, ppa->y, ppb->a.x, ppb->a.y, ppb->z);

        free (ppa);  /* if you allocate it, it is up to you to free it */
        free (ppb);
    }

    return 0;
}

当然,我没有清楚地了解你最终想要完成的事情。因此,为了利用您指定的值,减少了声明中间接的指针级别,以了解您尝试执行的操作。也就是说,将所有部分放在一起,你可以做类似以下的事情:

$ ./bin/structptrissue
ppa->x: 1
ppa->y: 2

ppb->a.x: 1
ppb->a.y: 2
ppb->z: 3

示例使用/输出

valgrind

内存错误检查

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

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

对于Linux $ valgrind ./bin/structptrissue ==19399== Memcheck, a memory error detector ==19399== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al. ==19399== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info ==19399== Command: ./bin/structptrissue ==19399== ppa->x: 1 ppa->y: 2 ppb->a.x: 1 ppb->a.y: 2 ppb->z: 3 ==19399== ==19399== HEAP SUMMARY: ==19399== in use at exit: 0 bytes in 0 blocks ==19399== total heap usage: 2 allocs, 2 frees, 20 bytes allocated ==19399== ==19399== All heap blocks were freed -- no leaks are possible ==19399== ==19399== For counts of detected and suppressed errors, rerun with: -v ==19399== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) 是正常的选择。每个平台都有类似的记忆检查器。它们都很简单易用,只需通过它运行程序即可。

$('input:not(:checked)').parent().removeClass("radio_checked");
$('input:not(:checked)').removeAttr("checked");
$('input:checked').parent().addClass("radio_checked");
$('input:checked').parent().addClass("selected");
$('input').click(function() {
  $('input:not(:checked)').parent().removeClass("radio_checked");
  $('input:not(:checked)').parent().removeClass("selected");
  $('input:not(:checked)').removeAttr("checked");
  $('input:checked').parent().addClass("radio_checked");
  $('input:checked').parent().addClass("selected");
});
$('input[type=radio]').hide();

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

仔细看看,如果您有其他问题,请告诉我。