-1

I have defined a structure StructA and function StructA createStructA(...) that creates and returns a new StructA. I want to have a function foo() that will explicitly return int error code as well as a new StructA that I want to use outside foo(). So it seems I need pointers to pointers:

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

Why does this code exit with segmentation fault? It appears it is due to malloc as if I comment out all other lines except for malloc line it still breaks with segfault

As the code above may seem unclear, here is MCVE of my problem:

#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;
}
micsza
  • 789
  • 1
  • 9
  • 16
  • Sorry, my typo - I do use sizeof ... edited – micsza May 20 '17 at 18:25
  • You also wrote `pa` instead of `pA` and `a` is local. – Bob__ May 20 '17 at 18:29
  • `*pA = &a;` --> `**pA = a;` – BLUEPIXY May 20 '17 at 18:30
  • 2
    What is `pa`? Is that meant to be `pA` overwriting the pointer to the memory you just allocated? If so, `StructA a` goes out of scope and life at the end of the function, so a pointer to `StructA a` used outside this function has no use. – Weather Vane May 20 '17 at 18:31
  • See: [**Do I cast the result of malloc?**](http://stackoverflow.com/q/605845/995714) "Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself. Questions without a clear problem statement are not useful to other readers. See: [**How to create a Minimal, Complete, and Verifiable example**](http://stackoverflow.com/help/mcve)." – David C. Rankin May 20 '17 at 18:32
  • I believe that this is not a good design. If you want to first allocate the struct yourself and then have some function `createStructA` to initialize it, then this function should receive the pointer to the allocated structure. Returning another struct and copying is quite bad practice, IMO. Making that right in the first place would have saved you the bug in your code. (BTW, the correct fix is `**pA = a;`) – nickie May 20 '17 at 18:33
  • The problem is I cannot even apply your solutions because the code breaks at malloc line (i.e. all other lines commented out) ... – micsza May 20 '17 at 18:39
  • 1
    As asked please post the MCVE. This was not your actual code (this is known because of its typos). – Weather Vane May 20 '17 at 18:41
  • If you have `struct StructA { ... }` then in C you need `struct StructA`, not just `StructA`, every time you refer to this type. – nickie May 20 '17 at 18:41
  • I submitted MCVE as requested – micsza May 20 '17 at 20:31

2 Answers2

3

*pA = &a set the *pA pointing to a local variable within function foo. When function returns, local variable is out of scope, therefore *pA becomes invalid.

Edit: Just read your new code. In this line, *ppa = malloc(sizeof(A)); // crashes with segfault here, ppa is not a valid pointer yet, you cannot dereference it by *ppa. Need to do ppa = (A**)malloc(sizeof(A*));first.

Actually, I make a guess here, this is want you want:



    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
    }

wyc
  • 351
  • 2
  • 11
  • I suppose that is one issue but there is some more as I cannot even correctly call malloc() ... I changed a code a bit and added more clear and working code being the abstract of my problem. – micsza May 20 '17 at 20:18
  • @micsza, I just modified the answer for the new code you put. – wyc May 20 '17 at 20:49
  • Please don't cast the result of `malloc`. It's useless at best, and dangerous at worst. – Quentin May 20 '17 at 23:28
  • @Quentin, depends if it's C or C++? Why does C++ require a cast for malloc() but C doesn't? http://stackoverflow.com/questions/3477741/why-does-c-require-a-cast-for-malloc-but-c-doesnt – wyc May 21 '17 at 07:49
  • @wyc well it's C, there's no mention of C++ here. – Quentin May 21 '17 at 10:22
2

In addition to the other corrections, if you will need ppa back in the calling function main, you have no way of returning a value to main. While there is exercise value in what you are doing, your actual goal is anything but clear. It is somewhat of an XY problem, see: What is the XY problem?.

That said, reading between the lines, and to make ppa available back in main, your parseB would have to somehow return a value. You could do so by changing the type to A *parseB (B **ppb).

Further, you seem to have a bit of confusion in whether to declare ppa and ppb as pointers or pointer-to-pointer-to-type. Given your initialization and use, it appears you want a pointer to both ppa and ppb. You would then pass the address of each to the functions parseB and parseA and dereference accordingly to allocate storage for their contents. Doing so, you could craft a parseB similar to:

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

note: realloc is used for the allocation, because you have no control over whether *ppb has been previously allocated within parseB itself (you do have control over what you send to parseA from parseB so malloc is fine in parseA)

To start this daisy-chain of allocation and assignment of values, your main could be written similar to:

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

Granted, I do not have a clear picture of what you are ultimately attempting to accomplish. So to make use of the values you assign, the pointer level of indirection at the declaration was reduced to make sense of what it looked like you were trying to do. That said, and putting all the pieces together, you could do something similar to the following:

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

Example Use/Output

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

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

Memory Error Check

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 insure you do not attempt to write beyond/outside the bounds of your allocated block of memory, 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.

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

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.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85